当前位置: 首页 > news >正文

站长之家综合查询工具易点设计

站长之家综合查询工具,易点设计,215做网站,住房和城乡建设部网站首页Linux网络编程2-多进程和多线程版本服务器 1.套接字相关函数的封装wrap.h wrap.c2.支持多并发的服务器3.多进程版本分析4.多进程版本实现5.多线程版本分析6.多线程版本实现 1.套接字相关函数的封装wrap.h wrap.c 像accept#xff0c;read这样的能够引起阻塞的函数#xff0c… Linux网络编程2-多进程和多线程版本服务器 1.套接字相关函数的封装wrap.h wrap.c2.支持多并发的服务器3.多进程版本分析4.多进程版本实现5.多线程版本分析6.多线程版本实现 1.套接字相关函数的封装wrap.h wrap.c 像acceptread这样的能够引起阻塞的函数若被信号打断由于信号的优先级较高, 会优先处理信号, 信号处理完成后会使accept或者read解除阻塞, 然后返回, 此时返回值为 -1设置errnoEINTR; 在/usr/include/asm-generic/errno.h文件中包含了errno所有的宏和对应的错误描述信息. #ifndef __WRAP_H_ #define __WRAP_H_ #include stdlib.h #include stdio.h #include unistd.h #include errno.h #include string.h #include sys/socket.h #include arpa/inet.h #include strings.hvoid 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 salen); int Listen(int fd, int backlog); int Socket(int family, int type, int protocol); ssize_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); int tcp4bind(short port,const char *IP); #endif#include stdlib.h #include stdio.h #include unistd.h #include errno.h #include string.h #include sys/socket.h #include arpa/inet.h #include strings.hvoid perr_exit(const char *s) {perror(s);exit(-1); }int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) {int n;again:if ((n accept(fd, sa, salenptr)) 0) {if ((errno ECONNABORTED) || (errno EINTR))goto again;elseperr_exit(accept error);}return n; }int Bind(int fd, const struct sockaddr *sa, socklen_t salen) {int n;if ((n bind(fd, sa, salen)) 0)perr_exit(bind error);return n; }int Connect(int fd, const struct sockaddr *sa, socklen_t salen) {int n;if ((n connect(fd, sa, salen)) 0)perr_exit(connect error);return n; }int Listen(int fd, int backlog) {int n;if ((n listen(fd, backlog)) 0)perr_exit(listen error);return n; }int Socket(int family, int type, int protocol) {int n;if ((n socket(family, type, protocol)) 0)perr_exit(socket error);return n; }ssize_t Read(int fd, void *ptr, size_t nbytes) {ssize_t n;again:if ( (n read(fd, ptr, nbytes)) -1) {if (errno EINTR)goto again;elsereturn -1;}return n; }ssize_t Write(int fd, const void *ptr, size_t nbytes) {ssize_t n;again:if ( (n write(fd, ptr, nbytes)) -1) {if (errno EINTR)goto again;elsereturn -1;}return n; }int Close(int fd) {int n;if ((n close(fd)) -1)perr_exit(close error);return n; }/*参三: 应该读取的字节数*/ ssize_t Readn(int fd, void *vptr, size_t n) {size_t nleft; //usigned int 剩余未读取的字节数ssize_t nread; //int 实际读到的字节数char *ptr;ptr vptr;nleft n;while (nleft 0) {if ((nread read(fd, ptr, nleft)) 0) {if (errno EINTR)nread 0;elsereturn -1;} else if (nread 0)break;nleft - nread;ptr nread;}return n - nleft; }ssize_t Writen(int fd, const void *vptr, size_t n) {size_t nleft;ssize_t nwritten;const char *ptr;ptr vptr;nleft n;while (nleft 0) {if ( (nwritten write(fd, ptr, nleft)) 0) {if (nwritten 0 errno EINTR)nwritten 0;elsereturn -1;}nleft - nwritten;ptr nwritten;}return n; }static ssize_t my_read(int fd, char *ptr) {static int read_cnt;static char *read_ptr;static char read_buf[100];if (read_cnt 0) { again:if ( (read_cnt read(fd, read_buf, sizeof(read_buf))) 0) {if (errno EINTR)goto again;return -1;} else if (read_cnt 0)return 0;read_ptr read_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;ptr vptr;for (n 1; n maxlen; n) {if ( (rc my_read(fd, c)) 1) {*ptr c;if (c \n)break;} else if (rc 0) {*ptr 0;return n - 1;} elsereturn -1;}*ptr 0;return n; }int tcp4bind(short port,const char *IP) {struct sockaddr_in serv_addr;int lfd Socket(AF_INET,SOCK_STREAM,0);bzero(serv_addr,sizeof(serv_addr));if(IP NULL){//如果这样使用 0.0.0.0,任意ip将可以连接serv_addr.sin_addr.s_addr INADDR_ANY;}else{if(inet_pton(AF_INET,IP,serv_addr.sin_addr.s_addr) 0){perror(IP);//转换失败exit(1);}}serv_addr.sin_family AF_INET;serv_addr.sin_port htons(port);Bind(lfd,(struct sockaddr *)serv_addr,sizeof(serv_addr));return lfd; }2.支持多并发的服务器 如何支持多个客户端—支持多并发的服务器 由于accept和read函数都会阻塞, 如当read的时候, 不能调用accept接受新的连接, 当accept阻塞等待的时候不能read读数据. 第一种方案: 使用多进程, 可以让父进程接受新连接, 让子进程处理与客户端通信 思路: 让父进程accept接受新连接, 然后fork子进程, 让子进程处理通信, 子进程处理完成后退出, 父进程使用SIGCHLD信号回收子进程. 第二种方案: 使用多线程, 让主线程接受新连接, 让子线程处理与客户端通信; 使用多线程要将线程设置为分离属性, 让线程在退出之后自己回收资源. 如何不使用多进程或者多线程完成多个客户端的连接请求 可以将accept和read函数设置为非阻塞, 调用fcntl函数可以将文件描述符设置为非阻塞, 让后再while循环中忙轮询. 3.多进程版本分析 阻塞函数在阻塞期间若收到信号会被信号终端errno设置为EINTR这个错误不应该看成一个错误. while(1) {cfd accept();while(1){n read(cfd, buf, sizeof(buf));if(n0){break;}} }解决办法1: 将cfd设置为非阻塞: fcntl 缺点假如有多个客户端连接请求, cfd只会保留最后一个文件描述符的值 解决方法2: 使用多进程: 让父进程监听接受新的连接, 子进程处理新的连接(接收和发送数据); 父进程还负责回收子进程 多进程版本思路子进程复制父进程的文件描述符 //1 创建socket, 得到一个监听的文件描述符lfd---socket() //2 将lfd和IP和端口port进行绑定-----bind(); //3 设置监听----listen() //4 进入while while(1) {//等待有新的客户端连接到来cfd accept();//fork一个子进程, 让子进程去处理数据pid fork();if(pid0){exit(-1);}else if(pid0){//关闭通信文件描述符cfdclose(cfd);}else if(pid0){//关闭监听文件描述符close(lfd);//收发数据while(1){//读数据n read(cfd, buf, sizeof(buf));if(n0){break;}//发送数据给对方write(cfd, buf, n);}close(cfd);//下面的exit必须有, 防止子进程再去创建子进程exit(0);} } close(lfd);还需要添加的功能: 父进程使用SIGCHLD信号完成对子进程的回收 注意点: accept或者read函数是阻塞函数会被信号打断此时不应该视为一个错误。errnoEINTR 父子进程能够共享的: 文件描述符(子进程复制父进程的文件描述符) mmap共享映射区 4.多进程版本实现 //多进程版本的网络服务器 #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include unistd.h #include arpa/inet.h #include netinet/in.h #include ctype.h #include wrap.hint main() {//创建socketint lfd Socket(AF_INET, SOCK_STREAM, 0);//绑定struct sockaddr_in serv;bzero(serv, sizeof(serv));serv.sin_family AF_INET;serv.sin_port htons(8888);serv.sin_addr.s_addr htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)serv, sizeof(serv));//设置监听Listen(lfd, 128);pid_t pid;int cfd;char sIP[16];socklen_t len;struct sockaddr_in client;while(1){//接受新的连接len sizeof(client);memset(sIP, 0x00, sizeof(sIP));cfd Accept(lfd, (struct sockaddr *)client, len);printf(client:[%s] [%d]\n, inet_ntop(AF_INET, client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));//接受一个新的连接, 创建一个子进程,让子进程完成数据的收发操作pid fork();if(pid0){perror(fork error);exit(-1);}else if(pid0){//关闭通信文件描述符cfdclose(cfd); }else if(pid0){//关闭监听文件描述符close(lfd);int i0;int n;char buf[1024];while(1){//读数据n Read(cfd, buf, sizeof(buf));if(n0){printf(read error or client closed, n[%d]\n, n);break;}//printf(client:[%s] [%d]\n, inet_ntop(AF_INET, client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));printf([%d]----:n[%d], buf[%s]\n, ntohs(client.sin_port), n, buf);//将小写转换为大写for(i0; in; i){buf[i] toupper(buf[i]);}//发送数据Write(cfd, buf, n);}//关闭cfdclose(cfd);exit(0);}}//关闭监听文件描述符close(lfd);return 0; }改进增加对子进程的回收 //多进程版本的服务器 #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include unistd.h #include arpa/inet.h #include netinet/in.h #include signal.h #include sys/wait.h #include ctype.h #include wrap.h//信号处理函数 void waitchild(int signo) {pid_t wpid;while(1){wpid waitpid(-1, NULL, WNOHANG);if(wpid0){printf(child exit, wpid[%d]\n, wpid);}else if(wpid0 || wpid-1){break;}} }int main() {//创建socketint lfd Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(int));//绑定-bindstruct sockaddr_in serv;serv.sin_family AF_INET;serv.sin_port htons(8888);serv.sin_addr.s_addr htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)serv, sizeof(serv));//监听-listenListen(lfd, 128);//阻塞SIGCHLD信号sigset_t mask;sigemptyset(mask);sigaddset(mask, SIGCHLD);sigprocmask(SIG_BLOCK, mask, NULL);int cfd;socklen_t len;char sIP[16];pid_t pid;struct sockaddr_in client;while(1){//等待客户端连接--acceptmemset(sIP, 0x00, sizeof(sIP));len sizeof(client);bzero(client, sizeof(client));cfd Accept(lfd, (struct sockaddr *)client, len); printf(client--[%s]:[%d]\n, inet_ntop(AF_INET, client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));//创建子进程pid fork();if(pid0){perror(fork error);close(lfd);return -1;} else if(pid0) //父进程{//关闭通信的文件描述符close(cfd);//注册SIGCHLD信号处理函数struct sigaction act;act.sa_handler waitchild;act.sa_flags 0;sigemptyset(act.sa_mask);sigaction(SIGCHLD, act, NULL);//解除对SIGCHLD信号的阻塞sigprocmask(SIG_UNBLOCK, mask, NULL);}else if(pid0) //子进程{//关闭监听文件描述符close(lfd); int i 0;int n 0;char buf[1024];while(1){memset(buf, 0x00, sizeof(buf));n Read(cfd, buf, sizeof(buf));if(n0) {printf(read error or client closed, n[%d]\n, n);break; }printf(read over, n[%d],buf[%s]\n, n, buf);for(i0; in; i){buf[i] toupper(buf[i]);}write(cfd, buf, n);}close(cfd);exit(0);}}//关闭监听文件描述符close(lfd);return 0; }5.多线程版本分析 //1 创建socket, 得到一个监听的文件描述符lfd---socket() //2 将lfd和IP和端口port进行绑定-----bind(); //3 设置监听----listen() //4 while循环 while(1) {//接受新的客户端连接请求cfd accept();//创建一个子线程pthread_create(threadID, NULL, thread_work, cfd);//设置线程为分离属性pthread_detach(threadID);} close(lfd);void *thread_work(void *arg) {//获得参数: 通信文件描述符int cfd *(int *)arg;while(1){//读数据n read(cfd, buf, sizeof(buf));if(n0){break;}//发送数据write(cfd, buf, n);}close(cfd); }问题: 1 子线程能否关闭lfd? 子线程不能关闭监听文件描述符lfd原因是子线程和主线程共享文件描述符而不是复制的. 2 主线程能否关闭cfd? 主线程不能关闭cfd主线程和子线程共享一个cfd而不是复制的close之后cfd就会被真正关闭. 3 多个子线程共享cfd, 会有什么问题发生? 只有一个子线程能够通信。 6.多线程版本实现 //多线程版本的高并发服务器 #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include unistd.h #include arpa/inet.h #include netinet/in.h #include ctype.h #include pthread.h #include wrap.h//子线程回调函数 void *thread_work(void *arg) {sleep(20);int cfd *(int *)arg;printf(cfd[%d]\n, cfd);int i;int n;char buf[1024];while(1){//read数据memset(buf, 0x00, sizeof(buf));n Read(cfd, buf, sizeof(buf));if(n0){printf(read error or client closed,n[%d]\n, n);break;}printf(n[%d], buf[%s]\n, n, buf);for(i0; in; i){buf[i] toupper(buf[i]);}//发送数据给客户端Write(cfd, buf, n); }//关闭通信文件描述符close(cfd);pthread_exit(NULL); }int main() {//创建socketint lfd Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(int));//绑定struct sockaddr_in serv;bzero(serv, sizeof(serv));serv.sin_family AF_INET;serv.sin_port htons(8888);serv.sin_addr.s_addr htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)serv, sizeof(serv));//设置监听Listen(lfd, 128);int cfd;pthread_t threadID;while(1){//接受新的连接cfd Accept(lfd, NULL, NULL);//创建子线程pthread_create(threadID, NULL, thread_work, cfd);//设置子线程为分离属性pthread_detach(threadID);}//关闭监听文件描述符close(lfd);return 0; }上面代码中accept接收3个连接创建3个子线程这些子线程共享threadId结构与cfd变量因此需要一个线程对应一个。数组。 // 创建100个INFO结构最多可以接收100个连接 struct INFO {int cfd;pthread_t threadID;struct sockaddr_in client; }; struct INFO info[100];//初始化INFO数组 for(i0; i100; i) {info[i].cfd-1; }// 接收连接以后从数组中分配一个INFO结构 for(i0; i100; i) {if(info[i].cfd-1){//这块内存可以使用} } if(i100) // 没有空闲的INFO结构拒绝接收新的连接 {//拒绝接受新的连接close(cfd); }//多线程版本的服务器 #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include unistd.h #include arpa/inet.h #include netinet/in.h #include ctype.h #include pthread.h #include wrap.htypedef struct info {int cfd; //若为-1表示可用, 大于0表示已被占用int idx;pthread_t thread;struct sockaddr_in client; }INFO;INFO thInfo[1024];//线程执行函数 void *thread_work(void *arg) {INFO *p (INFO *)arg;printf(idx[%d]\n, p-idx);char sIP[16];memset(sIP, 0x00, sizeof(sIP));printf(new client:[%s][%d]\n, inet_ntop(AF_INET, (p-client.sin_addr.s_addr), sIP, sizeof(sIP)), ntohs(p-client.sin_port));int n;int cfd p-cfd;struct sockaddr_in client;memcpy(client, (p-client), sizeof(client));char buf[1024];while(1){memset(buf, 0x00, sizeof(buf));//读数据n Read(cfd, buf, sizeof(buf));if(n0){printf(read error or client closed, n[%d]\n, n);Close(cfd);p-cfd -1; //设置为-1表示该位置可用pthread_exit(NULL);}for(int i0; in; i){buf[i] toupper(buf[i]);}//发送数据Write(cfd, buf, n);} }void init_thInfo() {int i 0;for(i0; i1024; i){thInfo[i].cfd -1;;} }int findIndex() {int i;for(i0; i1024; i){if(thInfo[i].cfd-1){break;}}if(i1024){return -1;}return i; }int main() {//创建socketint lfd Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(int));//绑定--将lfd 和 IP PORT绑定struct sockaddr_in serv;bzero(serv, sizeof(serv));serv.sin_family AF_INET;serv.sin_port htons(8888);serv.sin_addr.s_addr htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)serv, sizeof(serv));//监听Listen(lfd, 128);//初始化init_thInfo();int cfd;int ret;int idx;socklen_t len;pthread_t thread;struct sockaddr_in client;while(1){len sizeof(client);bzero(client, sizeof(client));//获得一个新的连接cfd Accept(lfd, (struct sockaddr *)client, len);//创建一个子进程, 让子进程处理连接---接收数据和发送数据//找数组中空闲的位置idx findIndex();if(idx-1){Close(cfd);continue;}//对空闲位置的元素的成员赋值thInfo[idx].cfd cfd;thInfo[idx].idx idx;memcpy(thInfo[idx].client, client, sizeof(client));//创建子线程---该子线程完成对数据的收发ret pthread_create(thInfo[idx].thread, NULL, thread_work, thInfo[idx]);if(ret!0){printf(create thread error:[%s]\n, strerror(ret));exit(-1);}//设置子线程为分离属性pthread_detach(thInfo[idx].thread);}Close(lfd);return 0;}
http://www.pierceye.com/news/548473/

相关文章:

  • 对网站建设心得进入公众号继续阅读怎么弄
  • 网站建设公司投诉电话可以先做网站后备案吗
  • 顺的品牌网站设计价位网站开发合同书
  • 用html5制作个人网站深圳百度国际大厦
  • 网络网站公司网站管理平台扩展插件
  • 个人小说网站怎么做把自己做的网页变成网站
  • led网站制作网站登录后不显示内容
  • 推广网站公司兴义市住房和城乡建设局网站
  • 营销网站建设的公司哪家好创建网站超链接
  • 苏州网站优化排名推广企业网站建设价格
  • 在线制作论坛网站wordpress建立好的网站
  • 网站的功能规范商城网站静态模板下载
  • 双语言网站源码制作网页的软件哪个好
  • 政务网站建设需求网站首页页面代码
  • 网站产品详情页怎么做的用服务器做网站空间
  • 河北网站制作报价长春市建设技工学校网站
  • 盘锦做网站专家常州网站建设企业网站制作
  • 关于建设网站的报告wordpress 视频 广告插件
  • 生态养殖网站模板网赌网站建设多少钱
  • wordpress is长沙百度提升优化
  • 福州网站建设哪个好网页被禁止浏览怎么解决
  • 缩短链接的网站磁力猫引擎
  • 佛山网站到首页排名网站开发招标任务书
  • 网站建设相关专业手机网站建设软件有哪些
  • 做网站编辑累吗平台推广策划
  • 景区网站模板深圳中建南方建设集团网站
  • 深圳市网站建设有补贴吗特殊教育学校网站建设方案
  • 专业电影网站建设建e全景效果图
  • 优惠券网站怎么做的哪里有网站制作
  • 单页网站搭建购买一级域名做网站