福建省建设局网站实名制,手机影视网站开发,asp.net做网站视频,网站地图怎么弄多进程并发服务器
基于该视频完成
11-多进程并发服务器思路分析_哔哩哔哩_bilibili
通过的是非阻塞忙轮询的方式实现的
和阻塞等待的区别就是#xff0c;阻塞是真的阻塞了#xff0c;而这个方式是一直在问有没有请求有没有请求 文章目录 多进程并发服务器1.核心思路阻塞是真的阻塞了而这个方式是一直在问有没有请求有没有请求 文章目录 多进程并发服务器1.核心思路功能2.代码实现warp.hwarp.cmulti_process_concurrency_sever.c运行图 3.代码解释 1.核心思路功能
实现一个服务器可以连接多个客户端每当accept函数等待到客户端进行连接时 就创建一个子进程;
核心思路让accept循环阻塞等待客户端每当有客户端连接时就fork子进程让子进程去和客户端进行通信父进程用于监听并使用信号捕捉回收子进程(子进程关闭用于监听的套接字lfd父进程关闭用于通信的cfd)
**功能**客户端输入小写字符串服务器转成大写返回给客户端
2.代码实现
warp.h
#ifndef __WRAP_H_
#define __WRAP_H_
#includesys/epoll.h
//#includeevent2/event.h
#includesys/select.h
#includestdio.h
#includestdlib.h
#includeunistd.h
#includefcntl.h
#includeerrno.h
#includestring.h
#includedirent.h
#includesys/stat.h
#includewait.h
#includesys/mman.h
#includesignal.h
#includepthread.h
#includesys/socket.h
#includearpa/inet.h
#includectype.h
#includestrings.h
#includenetinet/ip.h
#define SRV_PORT 1234void perr_exit(const char *s);
int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
size_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd,const void *ptr,size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);#endif
warp.c
#includewarp.hvoid perr_exit(const char *s)
{perror(s);exit(1);
}int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr)
{int n;
again:if((naccept(fd,sa,salenptr))0){if((errnoECONNABORTED)||(errnoEINTR))goto again;elseperr_exit(accept error);}return n;
}int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{int n;if((nbind(fd,sa,salen))0)perr_exit(bind error);return n;
}int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen)
{int n;nconnect(fd,sa,addrlen);if(n0){perr_exit(connect error);}return n;
}int Listen(int fd, int backlog)
{int n;if((nlisten(fd,backlog))0)perr_exit(listen error);return n;
}int Socket(int family, int type, int protocol)
{int n;if((nsocket(family,type,protocol))0)perr_exit(socket error);return n;
}size_t Read(int fd, void *ptr, size_t nbytes)
{ssize_t n;
again:if((nread(fd,ptr,nbytes))-1){if(errnoEINTR)goto again;elsereturn -1;}return n;
}ssize_t Write(int fd,const void *ptr,size_t nbytes)
{ssize_t n;
again:if((nwrite(fd,ptr,nbytes))-1){if(errnoEINTR)goto again;elsereturn -1;}return 0;
}int Close(int fd)
{int n;if((nclose(fd))-1)perr_exit(close error);return n;
}ssize_t Readn(int fd, void *vptr, size_t n)
{size_t nleft;ssize_t nread;char *ptr;ptrvptr;nleftn;while(nleft0){if((nreadread(fd,ptr,nleft))0){if(errnoEINTR)nread0;elsereturn -1;}else if(nread0)break;}
}
ssize_t Writen(int fd, const void *vptr, size_t n)
{size_t nleft;ssize_t nwritten;char *ptr;ptr(char *)vptr;nleftn;while(nleft0){if((nwrittenwrite(fd,ptr,nleft))0){if(nwritten0errnoEINTR)nwritten0;elsereturn -1;}nleft-nwritten;ptrnwritten;}return n;
}ssize_t my_read(int fd, char *ptr)
{static int read_cnt;static char *read_ptr;static char read_buf[100];if(read_cnt0){
again:if((read_cntread(fd,read_buf,sizeof(read_buf)))0){if(errnoEINTR)goto again;return -1;}else if(read_cnt0)return 0;read_ptrread_buf;}read_cnt--;*ptr*read_ptr;return 1;
}ssize_t Readline(int fd, void *vptr, size_t maxlen)
{ssize_t n,rc;char c,*ptr;ptrvptr;for(n1;nmaxlen;n){if((rcmy_read(fd,c))1){*ptrc;if(c\n)break;}else if(rc0){*ptr0;return n-1;}else return -1;}*ptr0;return n;
}multi_process_concurrency_sever.c
#includewarp.h//回调函数父进程用来完成子进程回收的
void catch_child(int signum)
{while((waitpid(0,NULL,WNOHANG))0);return ;
}int main(int argc,char *argv[])
{int lfd,cfd;pid_t pid;int ret;char buf[1024];struct sockaddr_in srv_addr,clt_addr;socklen_t clt_addr_len;//地址结构清0bzero(srv_addr,sizeof(srv_addr));srv_addr.sin_familyAF_INET;srv_addr.sin_porthtons(SRV_PORT);srv_addr.sin_addr.s_addrhtonl(INADDR_ANY);lfdSocket(AF_INET,SOCK_STREAM,0);Bind(lfd,(struct sockaddr*)srv_addr,sizeof(srv_addr));Listen(lfd,128);clt_addr_lensizeof(clt_addr);int i;while(1){cfdAccept(lfd,(struct sockaddr *)clt_addr,clt_addr_len);pidfork();if(pid0){perr_exit(fork error);}else if(pid0)//子进程{//关闭用不到的socketclose(lfd);for(;;){retRead(cfd,buf,sizeof(buf));if(ret0){Close(cfd);exit(1);}//逻辑处理部分for(i0;iret;i)buf[i]toupper(buf[i]);write(cfd,buf,ret);write(STDOUT_FILENO,buf,ret);}}else//父进程{//注册信号struct sigaction act;act.sa_handlercatch_child;sigemptyset(act.sa_mask);act.sa_flags0;retsigaction(SIGCHLD,act,NULL);if(ret!0){perr_exit(sigaction error);}Close(cfd);continue;}}return 0;
}gcc warp.c multi_process_concurrency_sever.c -o multi_process_concurrency_sever运行图
两个客户端访问服务器端 3.代码解释
1.指定的固定端口号为1234
也可以用argv接受ip和port参数
2.srv_addr.sin_addr.s_addrhtonl(INADDR_ANY);
sin_addr是struct sockaddr_in结构体中的一个嵌套结构体其成员s_addr用于存放 IP 地址信息以 32 位整数形式表示。htonl函数和htons类似不过它是将主机字节序的 32 位整数通常用于 IP 地址转换为网络字节序。INADDR_ANY是一个特殊的常量它表示服务器端套接字可以绑定到本机的任意可用 IP 地址上通过这行代码将转换为网络字节序后的INADDR_ANY值赋给了srv_addr结构体的sin_addr.s_addr成员使得服务器能够监听来自本机所有网络接口上对应端口的连接请求。
3.需要用到的调用都需要错误处理封装到warp.c后进行使用
4.父子进程是共享文件描述符表的
父子进程共享文件描述符表的含义 在 Unix 和 Linux 系统中当父进程创建子进程时子进程会继承父进程的文件描述符表。这意味着父子进程可以通过相同的文件描述符来访问同一个文件或 I/O 资源。文件描述符表是一个进程用于管理打开文件或其他 I/O 设备的表格其中每个文件描述符是一个整数索引对应着一个打开的文件或设备的相关信息如文件状态、读写位置等。例如父进程打开了一个文件得到文件描述符为 3当创建子进程后子进程也会有一个文件描述符为 3并且这个文件描述符在父子进程中都指向同一个打开的文件。这种共享机制使得父子进程可以方便地共享文件资源比如共同对一个文件进行读写操作。 父进程close(5)后子进程的情况 当父进程执行close(5)操作后只是父进程自己释放了文件描述符 5 所对应的资源。子进程的文件描述符表仍然保留文件描述符 5并且子进程仍然可以使用文件描述符 5 来访问对应的文件。原因是父子进程的文件描述符表虽然在创建子进程时是共享的但它们在操作文件描述符时是相互独立的。父进程的close操作不会影响子进程中已经继承的相同文件描述符。 子进程是否可以close(5)以及继续访问文件描述符 5 对应的文件 子进程可以执行close(5)操作。当子进程执行close(5)后它自己释放了文件描述符 5 对应的资源之后就不能再通过文件描述符 5 来访问原来对应的文件了。在没有执行close(5)之前子进程可以继续访问文件描述符 5 对应的文件。它可以进行正常的读写操作前提是该文件是以合适的模式打开的如可读可写模式并且文件的读写位置等状态是在父子进程间共享的。例如如果父进程已经读取了文件的一部分内容子进程通过相同的文件描述符 5 继续读取文件时会从父进程读取后的位置开始读取。
5.子进程做的事情
close(lfd)关闭监听套接字
read()
逻辑处理小写转大写
write()
6.父进程做的事情子进程的回收
close(cfd)
注册信号捕捉函数
在回调函数中完成子进程的回收