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

俄文网站建设方案网页实时翻译

俄文网站建设方案,网页实时翻译,上海公司查名一网通,竞价托管推广公司文章目录#xff1a; 一#xff1a;select 1.基础API select函数 思路分析 select优缺点 2.server.c 3.client.c 二#xff1a;poll 1.基础API poll函数 poll优缺点 read函数返回值 突破1024 文件描述符限制 2.server.c 3.client.c 三#xff1a;epoll …文章目录 一select 1.基础API  select函数 思路分析 select优缺点 2.server.c 3.client.c 二poll 1.基础API  poll函数  poll优缺点 read函数返回值 突破1024 文件描述符限制 2.server.c 3.client.c 三epoll 1.基础API epoll_create创建   epoll_ctl操作  epoll_wait阻塞 epoll实现多路IO转接思路 epoll优缺点  ctags使用 2.server.c 3.client.c 4.事件模型epoll 事件触发模型ET和LT 4.1 server.c 4.2 client.c 5.epoll 反应堆模型 select、poll以及epoll都是系统内核来对网络通信中的通信套接字(文件描述符)来进行监视 能够在与服务器连接的大量客户端中识别出与服务器请求了数据交换的客户端并把它们所对应的套接字通过函数返回交给服务器 此时服务器只需要和请求了数据交换的客户端进行通信即可而其它的套接字则不做任何处理因此比起服务器自身每次去轮询查询并处理每个套接字的效率要高很多 一select 1.基础API  select函数   原理 借助内核 select 来监听 客户端连接、数据通信事件//将给定的套接字fd从位图set中清除出去void FD_CLR(int fd,fd_set* set); FD_CLR4 rset; 将一个文件描述符从监听集合中 移除//检查给定的套接字fd是否在位图里面,返回值 在1 不在0int FD_ISSET(int fd,fd_set* set); FD_ISSET4rset; 判断一个文件描述符是否在监听集合中//将给定的套接字fd设置到位图set中 void FD_SET(int fd,fd_set* set); 将待监听的文件描述符添加到监听集合中 FD_SET(3, rset); FD_SET(5, rset); FD_SET(6, rset);//将整个位图set置零 void FD_ZERO(fd_set* set); fd_set rset; 清空一个文件描述符集FD_ZERO(rset);//select 是一个系统调用用于监控多个文件描述符sockets, files等的 I/O 活动 //它等待某个文件描述符集变为可读、可写或出现异常然后返回 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);nfds 监听 所有文件描述符中最大文件描述符1readfds 读 文件描述符监听集合。 传入、传出参数writefds 写 文件描述符监听集合。 传入、传出参数 NULLexceptfds异常 文件描述符监听集合 传入、传出参数 NULLtimeout 0 : 设置监听超时时长NULL阻塞监听0 非阻塞监听轮询返回值 0所有监听集合3个中 满足对应事件的总数0没有满足监听条件的文件描述符-1errno 思路分析 int maxfd 0lfd socket() ; 创建套接字maxfd lfd 备份bind(); 绑定地址结构listen(); 设置监听上限fd_set rset allset; 创建r读监听集合FD_ZERO(allset); 将r读监听集合清空FD_SET(lfd, allset); 将 lfd 添加至读集合中lfd文件描述符在监听期间没有满足读事件发生select返回的时候rset不会在集合中while1 {rset allset 保存监听集合ret select(lfd1 rset NULL NULL NULL); 监听文件描述符集合对应事件ifret 0 { 有监听的描述符满足对应事件//处理连接一次监听 if (FD_ISSET(lfd, rset)) { 1 在集合中,0不在cfd accept 建立连接返回用于通信的文件描述符maxfd cfdFD_SET(cfd, allset); 添加到监听通信描述符集合中}//处理通信剩下的for i lfd1 i 最大文件描述符; i{//嵌套FD_ISSET(i, rset) 有read、write事件read小 -- 大write();} }} select优缺点  当你只需要监听几个指定的套接字时, 需要对整个1024的数组进行轮询, 效率降低 缺点监听上限受文件描述符限制。 最大1024检测满足条件的fd自己添加业务逻辑提高小提高了编码难度如果监听的文件描述符比较散乱、而且数量不多效率会变低优点 跨平台win、linux、macOS、Unix、类Unix、mips 2.server.c #include stdio.h #include stdlib.h #include unistd.h #include string.h #include arpa/inet.h #include ctype.h#include wrap.h#define SERV_PORT 6666void FD_CLR(int fd,fd_set* set); //将给定的套接字fd从位图set中清除出去 int FD_ISSET(int fd,fd_set* set); //检查给定的套接字fd是否在位图里面,返回0或1 void FD_SET(int fd,fd_set* set); //将给定的套接字fd设置到位图set中 void FD_ZERO(fd_set* set); //将整个位图set置零int main(int argc, char *argv[]){int i, j, n, maxi;/*数组将需要轮询的客户端套接字放入数组client[FD_SETSIZE]防止遍历1024个文件描述符 FD_SETSIZE默认为1024*/int nready, client[FD_SETSIZE]; int listenFd, connectFd, maxFd, socketFd;char buf[BUFSIZ], str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16struct sockaddr_in serverAddr, clientAddr;socklen_t clientAddrLen;fd_set rset, allset; //rset读事件文件描述符集合,allset用来暂存/*得到监听套接字*/listenFd Socket(AF_INET, SOCK_STREAM, 0);/*定义两个集合,将listenFd放入allset集合当中*/fd_set rset, allset;FD_ZERO(allset); //将整个位图set置零//将给定的套接字fd设置到位图set中FD_SET(listenFd, allset); //将connectFd加入集合:构造select监控文件描述符集/*设置地址端口复用*/int opt 1;setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *)opt, sizeof(opt));/*填写服务器地址结构*/bzero(serverAddr, sizeof(serverAddr));serverAddr.sin_family AF_INET;serverAddr.sin_addr.s_addr htonl(INADDR_ANY);serverAddr.sin_port htons(SERVER_PORT);/*绑定服务器地址结构*/Bind(listenFd, (struct sockaddr *)serverAddr, sizeof(serverAddr));Listen(listenFd, 128);/*将listenFd设置为数组中最大的Fd*/maxFd listenFd; //起初 listenfd 即为最大文件描述符maxi -1; //将来用作client[]的下标, 初始值指向0个元素之前下标位置/*数组初始化自己的数组为-1*/for (i 0; i FD_SETSIZE; i)client[i] -1;while (1){/*把allset给rest,让他去用*/rset allset; //备份每次循环时都从新设置select监控信号集nready select(maxFd 1, rset, NULL, NULL, NULL); //使用select监听文件描述符集合对应事件if (nready -1) //出错返回perr_exit(select error);/*listen满足监听的事件:如果有了新的连接请求,得到connectFd,并将其放入自定义数组中*/if (FD_ISSET(listenFd, rset)){ //检查给定的套接字fd是否在位图里面,返回0或1clientAddrLen sizeof(clientAddr);//建立链接,不会阻塞connectFd Accept(listenFd, (struct sockaddr *)clientAddr, clientAddrLen);printf(Recived from %s at PORT %d\n, inet_ntop(AF_INET, (clientAddr.sin_addr.s_addr), str, sizeof(str)), ntohs(clientAddr.sin_port));for (i 0; i FD_SETSIZE; i)if (client[i] 0){ //找client[]中没有使用的位置client[i] connectFd; //保存accept返回的文件描述符到client[]里 break;}/*自定义数组满了:达到select能监控的文件个数上限 1024 */if(iFD_SETSIZE){fputs(Too many clients\n,stderr);exit(1);}/*connectFd加入监听集合:向监控文件描述符集合allset添加新的文件描述符connectFd*/FD_SET(connectFd, allset); //将给定的套接字fd设置到位图set中/*更新最大的Fd*/if (maxFd connectFd)maxFd connectFd;/*更新循环上限*/if(imaxi)maxii; //保证maxi存的总是client[]最后一个元素下标/*select返回1,说明只有建立连接请求,没有数据传送请求,跳出while循环剩余部分(下面的for循环轮询过程)*///如果只有listen事件,只需建立连接即可,无需数据传输,跳出循环剩余部分if (--nready 0)continue;}/*检测哪个clients 有数据就绪:select返回不是1,说明有connectFd有数据传输请求,遍历自定义数组*///否则,说明有数据传输需求for (i 0; i maxi; i){if((socketFdclient[i])0)continue;/*遍历检查*/if (FD_ISSET(socketFd, rset)){ //检查给定的套接字fd是否在位图里面,返回0或1/*read返回0说明传输结束,关闭连接:当client关闭链接时,服务器端也关闭对应链接*/if ((nread(socketFd,buf,sizeof(buf)))0){close(socketFd);//将给定的套接字fd从位图set中清除出去FD_CLR(socketFd, allset); //解除select对此文件描述符的监控client[i]-1;}else if(n0){for (j 0; j n; j)buf[j] toupper(buf[j]);write(socketFd, buf, n);write(STDOUT_FILENO, buf, n);}/*不懂:需要处理的个数减1?*/if(--nready0)break; //跳出for, 但还在while中}}}close(listenFd);return 0; } 3.client.c /* client.c */ #include stdio.h #include string.h #include stdlib.h #include unistd.h #include arpa/inet.h #include netinet/in.h#include wrap.h#define MAXLINE 80 #define SERV_PORT 6666int main(int argc, char *argv[]) {struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;if (argc ! 2) {printf(Enter: ./client server_IP\n);exit(1);}sockfd Socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, argv[1], servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);Connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));printf(------------connect ok----------------\n);while (fgets(buf, MAXLINE, stdin) ! NULL) {Write(sockfd, buf, strlen(buf));n Read(sockfd, buf, MAXLINE);if (n 0) {printf(the other side has been closed.\n);break;}elseWrite(STDOUT_FILENO, buf, n);}Close(sockfd);return 0; }二poll 这个函数是一个半成品用的很少  1.基础API  poll函数  int poll(struct pollfd *fds, nfds_t nfds, int timeout);fds监听的文件描述符传入传出【数组】struct pollfd { int fd 待监听的文件描述符 short events待监听的文件描述符对应的监听事件取值POLLIN、POLLOUT、POLLERRshort revnets传入时给0如果满足对应事件的话 返回 非0 -- POLLIN、POLLOUT、POLLERR}nfds: 监听数组的实际有效监听个数timeout: 0超时时长。单位毫秒-1阻塞等待0不阻塞返回值返回满足对应监听事件的文件描述符 总个数 poll优缺点 优点自带数组结构。 可以将 监听事件集合 和 返回事件集合 分离拓展 监听上限。 超出 1024限制缺点不能跨平台。 Linux无法直接定位满足监听事件的文件描述符 编码难度较大read函数返回值 0: 实际读到的字节数0 socket中表示对端关闭。close-1 如果 errno EINTR 被异常终端 需要重启如果 errno EAGIN 或 EWOULDBLOCK 以非阻塞方式读数据但是没有数据 需要再次读如果 errno ECONNRESET 说明连接被 重置 需要 close移除监听队列错误 突破1024 文件描述符限制 cat /proc/sys/fs/file-max —— 当前计算机所能打开的最大文件个数。 受硬件影响ulimit -a —— 当前用户下的进程默认打开文件描述符个数。 缺省为 1024修改打开 sudo vi /etc/security/limits.conf 写入* soft nofile 65536 -- 设置默认值 可以直接借助命令修改。 【注销用户使其生效】* hard nofile 100000 -- 命令修改上限 2.server.c #include stdio.h #include stdlib.h #include string.h #include netinet/in.h #include arpa/inet.h #include poll.h #include errno.h #include wrap.h#define MAXLINE 80 #define SERV_PORT 6666 #define OPEN_MAX 1024int main(int argc,char* argv[]){int ret0;/*poll函数返回值*/int nready0;int i,j,maxi;int connectFd,listenFd,socketFd;ssize_t n;char buf[MAXLINE];char str[INET_ADDRSTRLEN];socklen_t clientLen;/*创建结构体数组*/ struct pollfd client[OPEN_MAX];/*创建客户端地址结构和服务器地址结构*/struct sockaddr_in clientAddr,serverAddr;/*得到监听套接字listenFd*/listenFdSocket(AF_INET,SOCK_STREAM,0);/*设置地址可复用*/int opt0;retsetsockopt(listenFd,SOL_SOCKET,SO_REUSEADDR,(void*)opt,sizeof(opt));if(ret-1)perr_exit(setsockopt error);/*向服务器地址结构填入内容*/bzero(serverAddr,sizeof(serverAddr));serverAddr.sin_familyAF_INET;serverAddr.sin_addr.s_addrhtonl(INADDR_ANY);serverAddr.sin_porthtons(SERVER_PORT);/*绑定服务器地址结构到监听套接字,并设置监听上限*/Bind(listenFd,(const struct sockaddr*)serverAddr,sizeof(serverAddr));Listen(listenFd,128);/*初始化第一个pollfd为监听套接字*/client[0].fdlistenFd; //listenfd监听普通读事件 client[0].eventsPOLLIN; //事件已经准备好被读取或处理/*将pollfd数组的余下内容的fd文件描述符属性置为-1*/for(i1;iOPEN_MAX;i)client[i].fd-1; //用-1初始化client[]里剩下元素maxi0; //client[]数组有效元素中最大元素下标while(1){/*nready是有多少套接字有POLLIN请求*/nreadypoll(client,maxi1,-1); //阻塞if(nready-1)perr_exit(poll error);/*如果listenFd的revents有POLLIN请求,则调用Accept函数得到connectFd*/if(client[0].reventsPOLLIN){ //有客户端链接请求clientLensizeof(clientAddr);connectFdAccept(listenFd,(struct sockaddr*)clientAddr,clientLen);/*打印客户端地址结构信息*/printf(Received from %s at PORT %d\n,inet_ntop(AF_INET,(clientAddr.sin_addr.s_addr),str,sizeof(str)),ntohs(clientAddr.sin_port));/*将创建出来的connectFd加入到pollfd数组中*/for(i1;iOPEN_MAX;i)if(client[i].fd0){//找到client[]中空闲的位置存放accept返回的connfd client[i].fdconnectFd; break;}if(iOPEN_MAX)perr_exit(Too many clients,Im going to die...);/*当没有错误时,将对应的events设置为POLLIN*/client[i].eventsPOLLIN; //设置刚刚返回的connfd监控读事件if(imaxi) maxii; //更新client[]中最大元素下标if(--nready0)continue; //没有更多就绪事件时,继续回到poll阻塞}/*开始从1遍历pollfd数组*/for(i1;imaxi;i){ //检测client[] /*到结尾了或者有异常*/if((socketFdclient[i].fd)0)continue;/*第i个客户端有连接请求,进行处理 read*/if(client[i].reventsPOLLIN){if((nread(socketFd,buf,sizeof(buf)))0){/*出错时进一步判断errno*/if(errnoECONNRESET){printf(client[%d] aborted connection\n,i);close(socketFd);client[i].fd-1;}elseperr_exit(read error);}else if(n0){/*read返回0,说明读到了结尾,关闭连接*/printf(client[%d] closed connection\n,i);close(socketFd);client[i].fd-1;}else{/*数据处理*/for(j0;jn;j)buf[j]toupper(buf[j]);Writen(STDOUT_FILENO,buf,n);Writen(socketFd,buf,n);}if(--nready0)break;}}}return 0; } 3.client.c /* client.c */ #include stdio.h #include string.h #include unistd.h #include netinet/in.h #include wrap.h#define MAXLINE 80 #define SERV_PORT 6666int main(int argc, char *argv[]) {struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;sockfd Socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);Connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));while (fgets(buf, MAXLINE, stdin) ! NULL) {Write(sockfd, buf, strlen(buf));n Read(sockfd, buf, MAXLINE);if (n 0)printf(the other side has been closed.\n);elseWrite(STDOUT_FILENO, buf, n);}Close(sockfd);return 0; } 三epoll epoll是Linux下多路复用IO接口select/poll的增强版本它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率都连接但不发送数据  1.基础API 红黑树 lfd数据连接cfd数据通信 epoll_create创建   epoll_ctl操作  epoll_wait阻塞 int epoll_create(int size); 创建一棵监听红黑树size创建的红黑树的监听节点数量仅供内核参考返回值成功指向新创建的红黑树的根节点的 fd失败 -1 errnoint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 操作控制监听红黑树epfdepoll_create 函数的返回值 epfdop 对该监听红黑数所做的操作EPOLL_CTL_ADD 添加fd到 监听红黑树EPOLL_CTL_MOD 修改fd在 监听红黑树上的监听事件EPOLL_CTL_DEL 将一个fd 从监听红黑树上摘下取消监听fd待监听的fd event本质struct epoll_event 结构体 地址成员 eventsEPOLLIN / EPOLLOUT / EPOLLERR EPOLLIN 表示对应的文件描述符可以读包括对端SOCKET正常关闭EPOLLOUT 表示对应的文件描述符可以写EPOLLPRI 表示对应的文件描述符有紧急的数据可读这里应该表示有带外数据到来EPOLLERR 表示对应的文件描述符发生错误EPOLLHUP 表示对应的文件描述符被挂断EPOLLET 将EPOLL设为边缘触发(Edge Triggered)模式这是相对于水平触发(Level Triggered)而言的EPOLLONESHOT只监听一次事件当监听完这次事件之后如果还需要继续监听这个socket的话需要再次把这个socket加入到EPOLL队列里成员 typedef union epoll_data 联合体共用体int fd; 对应监听事件的 fdvoid *ptr uint32_t u32;uint64_t u64; 返回值成功 0 失败 -1 errnoint epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 阻塞监听epfdepoll_create 函数的返回值 epfdevents传出参数【数组】 满足监听条件的 哪些 fd 结构体maxevents数组 元素的总个数 1024不是字节数 struct epoll_event evnets[1024]timeout-1: 阻塞————通过等待某些特定条件出现来实现的而在等待的过程中程序的其他部分都会被暂停执行0不阻塞0: 超时时间 毫秒read返回值 0: 满足监听的 总个数可以用作循环上限0没有fd满足监听事件-1失败errno epoll实现多路IO转接思路 lfd socket; 监听连接事件lfd bind(); listen();int epfd epoll_create(1024); epfd, 监听红黑树的树根struct epoll_event tep, ep[1024]; tep, 用来设置单个fd属性 ep是epoll_wait() 传出的满足监听事件的数组tep.events EPOLLIN; 初始化 lfd的监听属性_文件描述符可以读tep.data.fd lfd epoll_ctl(epfd EPOLL_CTL_ADD, lfd, tep); 将 lfd 添加到监听红黑树上while (1) {ret epoll_wait(epfd ep1024 -1); 阻塞监听for (i 0; i ret; i) { //lfd数据连接if (ep[i].data.fd lfd) { lfd 满足读事件有新的客户端发起连接请求cfd Accept();tep.events EPOLLIN; 初始化 cfd的监听属性_文件描述符可以读tep.data.fd cfd;epoll_ctl(epfd EPOLL_CTL_ADD, cfd, tep); 将 cfd 添加到监听红黑树上}//cfd数据通信else { cfd 们 满足读事件 有客户端写数据来n read(ep[i].data.fd, buf, sizeof(buf));if ( n 0) {close(ep[i].data.fd);epoll_ctl(epfd EPOLL_CTL_DEL, ep[i].data.fd , NULL); 将关闭的cfd从监听树上摘下} else if n 0 {小--大write(ep[i].data.fd, buf, n);}}} } epoll优缺点  优点高效。突破1024文件描述符缺点不能跨平台。 Linux ctags使用 是vim下方便代码阅读的工具1ctags ./* -R在项目目录下生成ctags文件;Ctrl]跳转到函数定义的位置;Ctrlt返回此前的跳转位置;Ctrlo屏幕左边列出文件列表, 再按关闭;F4屏幕右边列出函数列表, 再按关闭;(还是VSCode比较香) 2.server.c #include 033-035_wrap.h#define SERVER_PORT 9527 #define MAXLINE 80 #define OPEN_MAX 1024int main(int argc,char* argv[]){int i0,n0,num0;int clientAddrLen0;int listenFd0,connectFd0,socketFd0;ssize_t nready,efd,res;char buf[MAXLINE],str[INET_ADDRSTRLEN];struct sockaddr_in serverAddr,clientAddr;/*创建一个临时节点temp和一个数组ep*/struct epoll_event temp;struct epoll_event ep[OPEN_MAX];/*创建监听套接字*/listenFdSocket(AF_INET,SOCK_STREAM,0);/*设置地址可复用*/int opt1;setsockopt(listenFd,SOL_SOCKET,SO_REUSEADDR,(void*)opt,sizeof(opt));/*初始化服务器地址结构*/bzero(serverAddr,sizeof(serverAddr));serverAddr.sin_familyAF_INET;serverAddr.sin_addr.s_addrhtonl(INADDR_ANY);serverAddr.sin_porthtons(SERVER_PORT);/*绑定服务器地址结构*/Bind(listenFd,(const struct sockaddr*)serverAddr,sizeof(serverAddr));/*设置监听上限*/Listen(listenFd,128);/*创建监听红黑树树根*/efdepoll_create(OPEN_MAX);if(efd-1)perr_exit(epoll_create error);/*将listenFd加入监听红黑树中*/temp.eventsEPOLLIN;temp.data.fdlistenFd;resepoll_ctl(efd,EPOLL_CTL_ADD,listenFd,temp);if(res-1)perr_exit(epoll_ctl error);while(1){/*阻塞监听写事件*/nreadyepoll_wait(efd,ep,OPEN_MAX,-1);if(nready-1)perr_exit(epoll_wait error);/*轮询整个数组(红黑树)*/for(i0;inready;i){if(!(ep[i].eventsEPOLLIN))continue;/*如果是建立连接请求*/// lfd 满足读事件有新的客户端发起连接请求if(ep[i].data.fdlistenFd){clientAddrLensizeof(clientAddr);connectFdAccept(listenFd,(struct sockaddr*)clientAddr,clientAddrLen);printf(Received from %s at PORT %d\n,inet_ntop(AF_INET,clientAddr.sin_addr.s_addr,str,sizeof(str)),ntohs(clientAddr.sin_port));printf(connectFd%d,client[%d]\n,connectFd,num);/*将新创建的连接套接字加入红黑树*///初始化 cfd的监听属性_文件描述符可以读temp.eventsEPOLLIN;temp.data.fdconnectFd;resepoll_ctl(efd,EPOLL_CTL_ADD,connectFd,temp);if(res-1)perr_exit(epoll_ctl errror);}else{/*不是建立连接请求,是数据处理请求*/socketFdep[i].data.fd;//cfd 们 满足读事件 有客户端写数据来nread(socketFd,buf,sizeof(buf));/*读到0说明客户端关闭*///已经读到结尾if(n0){resepoll_ctl(efd,EPOLL_CTL_DEL,socketFd,NULL);if(res-1)perr_exit(epoll_ctl error);close(socketFd);printf(client[%d] closed connection\n,socketFd);//报错}else if(n0){ /*n0报错*/perr_exit(read n0 error);// 将关闭的cfd从监听树上摘下resepoll_ctl(efd,EPOLL_CTL_DEL,socketFd,NULL);close(socketFd);// 0实际读到的字节数}else{/*数据处理*/for(i0;in;i)buf[i]toupper(buf[i]);write(STDOUT_FILENO,buf,n);Writen(socketFd,buf,n);}}}}close(listenFd);close(efd);return 0; } 3.client.c /* client.c */ #include stdio.h #include string.h #include unistd.h #include netinet/in.h #include wrap.h#define MAXLINE 80 #define SERV_PORT 6666int main(int argc, char *argv[]) {struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;sockfd Socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);Connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));while (fgets(buf, MAXLINE, stdin) ! NULL) {Write(sockfd, buf, strlen(buf));n Read(sockfd, buf, MAXLINE);if (n 0)printf(the other side has been closed.\n);elseWrite(STDOUT_FILENO, buf, n);}Close(sockfd);return 0; } 4.事件模型epoll 事件触发模型ET和LT ET工作模式边沿触发————只有数据到来才触发不管缓存区中是否还有数据缓冲区剩余未读尽的数据不会导致作用当文件描述符从未就绪变为就绪时内核会通过epoll告诉你一次喊你就绪直到你做操作导致那个文件描述符不再为就绪状态缓冲区未读尽的数据不会导致epoll_wait返回, 新的数据写入才会触发等文件描述符不再为就绪状态 struct epoll_event eventevent.events EPOLLIN | EPOLLETLT工作模式水平触发————只要有数据都会触发默认采用模式作用内核告诉你一个文件描述符是否就绪然后可以对这个就绪的fd进行io操作如果你不做任何操作内核还会继续通知你缓冲区未读尽的数据会导致epoll_wait返回继续通知你结论epoll 的 ET模式 高效模式但是只支持 非阻塞模式--- 忙轮询用于在计算机系统中处理硬件中断忙轮询是一种不进入内核的方式它在用户空间中轮询检测硬件状态及时响应硬件的中断请求避免CPU在中断服务程序中处理完所有的中断请求后又再次触发中断struct epoll_event event;event.events EPOLLIN | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_ADD, cfd event); int flg fcntl(cfd, F_GETFL); 非阻塞flg | O_NONBLOCK;fcntl(cfd, F_SETFL, flg); 代码实现  #include stdio.h #include stdlib.h #include sys/epoll.h #include errno.h #include unistd.h#define MAXLINE 10int main(int argc, char *argv[]) {int efd, i;int pfd[2];pid_t pid;char buf[MAXLINE], ch a;pipe(pfd);pid fork();if (pid 0) { //子 写close(pfd[0]);while (1) {//aaaa\nfor (i 0; i MAXLINE/2; i)buf[i] ch;buf[i-1] \n;ch;//bbbb\nfor (; i MAXLINE; i)buf[i] ch;buf[i-1] \n;ch;//aaaa\nbbbb\nwrite(pfd[1], buf, sizeof(buf));sleep(5);}close(pfd[1]);} else if (pid 0) { //父 读struct epoll_event event;struct epoll_event resevent[10]; //epoll_wait就绪返回eventint res, len;close(pfd[1]);efd epoll_create(10);event.events EPOLLIN | EPOLLET; // ET 边沿触发// event.events EPOLLIN; // LT 水平触发 (默认)event.data.fd pfd[0];epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], event);while (1) {res epoll_wait(efd, resevent, 10, -1);printf(res %d\n, res);if (resevent[0].data.fd pfd[0]) {len read(pfd[0], buf, MAXLINE/2);write(STDOUT_FILENO, buf, len);}}close(pfd[0]);close(efd);} else {perror(fork);exit(-1);}return 0; } 4.1 server.c #include stdio.h #include string.h #include netinet/in.h #include arpa/inet.h #include signal.h #include sys/wait.h #include sys/types.h #include sys/epoll.h #include unistd.h#define MAXLINE 10 #define SERV_PORT 9000int main(void) {struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int efd;listenfd socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;servaddr.sin_addr.s_addr htonl(INADDR_ANY);servaddr.sin_port htons(SERV_PORT);bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr));listen(listenfd, 20);struct epoll_event event;struct epoll_event resevent[10];int res, len;efd epoll_create(10);event.events EPOLLIN | EPOLLET; /* ET 边沿触发 *///event.events EPOLLIN; /* 默认 LT 水平触发 */printf(Accepting connections ...\n);cliaddr_len sizeof(cliaddr);connfd accept(listenfd, (struct sockaddr *)cliaddr, cliaddr_len);printf(received from %s at PORT %d\n,inet_ntop(AF_INET, cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));event.data.fd connfd;epoll_ctl(efd, EPOLL_CTL_ADD, connfd, event);while (1) {res epoll_wait(efd, resevent, 10, -1);printf(res %d\n, res);if (resevent[0].data.fd connfd) {len read(connfd, buf, MAXLINE/2); //readn(500) write(STDOUT_FILENO, buf, len);}}return 0; }4.2 client.c #include stdio.h #include string.h #include unistd.h #include arpa/inet.h #include netinet/in.h#define MAXLINE 10 #define SERV_PORT 9000int main(int argc, char *argv[]) {struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, i;char ch a;sockfd socket(AF_INET, SOCK_STREAM, 0);bzero(servaddr, sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET, 127.0.0.1, servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr));while (1) {//aaaa\nfor (i 0; i MAXLINE/2; i)buf[i] ch;buf[i-1] \n;ch;//bbbb\nfor (; i MAXLINE; i)buf[i] ch;buf[i-1] \n;ch;//aaaa\nbbbb\nwrite(sockfd, buf, sizeof(buf));sleep(5);}close(sockfd);return 0; }5.epoll 反应堆模型 作用提高网络IO处理的效率epoll ET模式 非阻塞、轮询 void *ptrvoid *ptr指向结构体该结构体包含socket、地址、端口等信息原来epoll实现多路IO转接思路socket、bind、listen -- epoll_create 创建监听 红黑树 -- 返回 epfd -- epoll_ctl() 向树上添加一个监听fd -- while1---- epoll_wait 监听 -- 对应监听fd有事件产生 -- 返回 监听满足数组。 -- 判断返回数组元素 -- lfd满足 -- Accept -- cfd 满足 -- read() --- 小-大 -- write回去反应堆不但要监听 cfd 的读事件、还要监听cfd的写事件socket、bind、listen -- epoll_create 创建监听 红黑树 -- 返回 epfd -- epoll_ctl() 向树上添加一个监听fd -- while1---- epoll_wait 监听 -- 对应监听fd有事件产生 -- 返回 监听满足数组。 -- 判断返回数组元素 -- lfd满足 -- Accept -- cfd 满足 -- read() --- 小-大 -- cfd从监听红黑树上摘下 -- EPOLLOUT -- 回调函数 -- epoll_ctl() -- EPOLL_CTL_ADD 重新放到红黑上监听“写”事件-- 等待 epoll_wait 返回 -- 说明 cfd 可写 -- write回去 -- cfd从监听红黑树上摘下 -- EPOLLIN -- epoll_ctl() -- EPOLL_CTL_ADD 重新放到红黑上监听“读”事件 -- epoll_wait 监听eventset函数设置回调函数lfd -- acceptconn()cfd -- recvdata();cfd -- senddata();eventadd函数将一个fd 添加到 监听红黑树设置监听读事件还是监听写事件网络编程中 read --- recv() write --- send(); epoll基于非阻塞I/O事件驱动 /**epoll基于非阻塞I/O事件驱动*/ #include stdio.h #include sys/socket.h #include sys/epoll.h #include arpa/inet.h #include fcntl.h #include unistd.h #include errno.h #include string.h #include stdlib.h #include time.h#define MAX_EVENTS 1024 //监听上限数 #define BUFLEN 4096 #define SERV_PORT 8080 //默认端口号void recvdata(int fd, int events, void *arg); void senddata(int fd, int events, void *arg);/* 描述就绪文件描述符相关信息 */ struct myevent_s {int fd; //要监听的文件描述符int events; //对应的监听事件void *arg; //泛型参数void (*call_back)(int fd, int events, void *arg); //回调函数int status; //是否在监听:1-在红黑树上(监听), 0-不在(不监听)char buf[BUFLEN];int len;long last_active; //记录每次加入红黑树 g_efd 的时间值 };int g_efd; //全局变量, 保存epoll_create返回的文件描述符 struct myevent_s g_events[MAX_EVENTS1]; //自定义结构体类型数组. 1--listen fd/*将结构体 myevent_s 成员变量 初始化赋值*/void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg){ev-fd fd;ev-call_back call_back; //设置回调函数ev-events 0;ev-arg arg;ev-status 0;memset(ev-buf, 0, sizeof(ev-buf));ev-len 0;ev-last_active time(NULL); //调用eventset函数的时间return;}/* 向 epoll监听的红黑树 添加一个 文件描述符 *///eventadd函数: 将一个fd添加到监听红黑树, 设置监听读事件还是写事件//eventadd(efd, EPOLLIN, g_events[MAX_EVENTS]);void eventadd(int efd, int events, struct myevent_s *ev){struct epoll_event epv {0, {0}};int op;epv.data.ptr ev;epv.events ev-events events; //EPOLLIN 或 EPOLLOUTif (ev-status 0) { //已经在红黑树 g_efd 里op EPOLL_CTL_ADD; //将其加入红黑树 g_efd, 并将status置1ev-status 1;}if (epoll_ctl(efd, op, ev-fd, epv) 0) //实际添加/修改printf(event add failed [fd%d], events[%d]\n, ev-fd, events);elseprintf(event add OK [fd%d], op%d, events[%0X]\n, ev-fd, op, events);return ;}/* 从epoll 监听的 红黑树中删除一个 文件描述符*/void eventdel(int efd, struct myevent_s *ev){struct epoll_event epv {0, {0}};if (ev-status ! 1) //不在红黑树上return ;//epv.data.ptr ev;epv.data.ptr NULL;ev-status 0; //修改状态epoll_ctl(efd, EPOLL_CTL_DEL, ev-fd, epv); //从红黑树 efd 上将 ev-fd 摘除return ;}/* 当有文件描述符就绪, epoll返回, 调用该函数 与客户端建立链接 */void acceptconn(int lfd, int events, void *arg){struct sockaddr_in cin;socklen_t len sizeof(cin);int cfd, i;if ((cfd accept(lfd, (struct sockaddr *)cin, len)) -1) {if (errno ! EAGAIN errno ! EINTR) {/* 暂时不做出错处理 */}printf(%s: accept, %s\n, __func__, strerror(errno));return ;}do {for (i 0; i MAX_EVENTS; i) //从全局数组g_events中找一个空闲元素if (g_events[i].status 0) //类似于select中找值为-1的元素break; //跳出 forif (i MAX_EVENTS) {printf(%s: max connect limit[%d]\n, __func__, MAX_EVENTS);break; //跳出do while(0) 不执行后续代码}int flag 0;if ((flag fcntl(cfd, F_SETFL, O_NONBLOCK)) 0) { //将cfd也设置为非阻塞printf(%s: fcntl nonblocking failed, %s\n, __func__, strerror(errno));break;}/* 给cfd设置一个 myevent_s 结构体, 回调函数 设置为 recvdata */eventset(g_events[i], cfd, recvdata, g_events[i]); eventadd(g_efd, EPOLLIN, g_events[i]); //将cfd添加到红黑树g_efd中,监听读事件} while(0);printf(new connect [%s:%d][time:%ld], pos[%d]\n, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), g_events[i].last_active, i);return ;}//epoll反应堆-wait被触发后read和write回调及监听 void recvdata(int fd, int events, void *arg){struct myevent_s *ev (struct myevent_s *)arg;int len;len recv(fd, ev-buf, sizeof(ev-buf), 0); //读文件描述符, 数据存入myevent_s成员buf中eventdel(g_efd, ev); //将该节点从红黑树上摘除if (len 0) {ev-len len;ev-buf[len] \0; //手动添加字符串结束标记printf(C[%d]:%s\n, fd, ev-buf);eventset(ev, fd, senddata, ev); //设置该 fd 对应的回调函数为 senddataeventadd(g_efd, EPOLLOUT, ev); //将fd加入红黑树g_efd中,监听其写事件} else if (len 0) {close(ev-fd);/* ev-g_events 地址相减得到偏移元素位置 */printf([fd%d] pos[%ld], closed\n, fd, ev-g_events);} else {close(ev-fd);printf(recv[fd%d] error[%d]:%s\n, fd, errno, strerror(errno));}return;}void senddata(int fd, int events, void *arg){struct myevent_s *ev (struct myevent_s *)arg;int len;len send(fd, ev-buf, ev-len, 0); //直接将数据 回写给客户端。未作处理eventdel(g_efd, ev); //从红黑树g_efd中移除if (len 0) {printf(send[fd%d], [%d]%s\n, fd, len, ev-buf);eventset(ev, fd, recvdata, ev); //将该fd的 回调函数改为 recvdataeventadd(g_efd, EPOLLIN, ev); //从新添加到红黑树上 设为监听读事件} else {close(ev-fd); //关闭链接printf(send[fd%d] error %s\n, fd, strerror(errno));}return ;}/*创建 socket, 初始化lfd */void initlistensocket(int efd, short port){struct sockaddr_in sin;//将socket设为lfd非阻塞int lfd socket(AF_INET, SOCK_STREAM, 0);fcntl(lfd, F_SETFL, O_NONBLOCK); //设置地址结构memset(sin, 0, sizeof(sin)); //bzero(sin, sizeof(sin))sin.sin_family AF_INET;sin.sin_addr.s_addr INADDR_ANY;sin.sin_port htons(port);bind(lfd, (struct sockaddr *)sin, sizeof(sin));listen(lfd, 20);/* void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *), void *arg); *//*把g_events数组的最后一个元素设置为lfd,回调函数设置为acceptconn*/eventset(g_events[MAX_EVENTS], lfd, acceptconn, g_events[MAX_EVENTS]);/* void eventadd(int efd, int events, struct myevent_s *ev) *//*挂上树*/eventadd(efd, EPOLLIN, g_events[MAX_EVENTS]);return ;}int main(int argc, char *argv[]) {/*选择默认端口号或指定端口号*/unsigned short port SERV_PORT;if (argc 2)//使用用户指定端口.如未指定,用默认端口port atoi(argv[1]); //创建红黑树,返回给全局 g_efdg_efd epoll_create(MAX_EVENTS1); if (g_efd 0)printf(create efd in %s err %s\n, __func__, strerror(errno));//初始化监听socketinitlistensocket(g_efd, port); //创建一个系统的epoll_event的数组,与my_events的规模相同struct epoll_event events[MAX_EVENTS1]; //保存已经满足就绪事件的文件描述符数组 printf(server running:port[%d]\n, port);int checkpos 0, i;while (1) {/* 超时验证每次测试100个链接不测试listenfd 当客户端60秒内没有和服务器通信则关闭此客户端链接 */ long now time(NULL); //当前时间for (i 0; i 100; i, checkpos) { //一次循环检测100个。 使用checkpos控制检测对象if (checkpos MAX_EVENTS)checkpos 0;if (g_events[checkpos].status ! 1) //不在红黑树 g_efd 上continue;long duration now - g_events[checkpos].last_active; //时间间隔,客户端不活跃的世间if (duration 60) {close(g_events[checkpos].fd); //关闭与该客户端链接printf([fd%d] timeout\n, g_events[checkpos].fd);eventdel(g_efd, g_events[checkpos]); //将该客户端 从红黑树 g_efd移除}}/*监听红黑树g_efd, 将满足的事件的文件描述符加至events数组中, 1秒没有事件满足, 返回 0*/int nfd epoll_wait(g_efd, events, MAX_EVENTS1, 1000);if (nfd 0) {printf(epoll_wait error, exit\n);break;}for (i 0; i nfd; i) {/*使用自定义结构体myevent_s类型指针, 接收 联合体data的void *ptr成员*/struct myevent_s *ev (struct myevent_s *)events[i].data.ptr; //cfd从监听红黑树上摘下 if ((events[i].events EPOLLIN) (ev-events EPOLLIN)) { //读就绪事件ev-call_back(ev-fd, events[i].events, ev-arg);//lfd EPOLLIN }if ((events[i].events EPOLLOUT) (ev-events EPOLLOUT)) { //写就绪事件ev-call_back(ev-fd, events[i].events, ev-arg);}}}/* 退出前释放所有资源 */return 0; }
http://www.sadfv.cn/news/306382/

相关文章:

  • 个人设计网站论文摘要淮北论坛人才招聘网
  • 扮家家室内设计网青岛网站制作seo
  • 免费的域名注册网站二级建造师注册查询系统
  • 安徽网站设计平台aws wordpress 站群
  • 如何设置手机网站主页企业品牌网站建设多少钱
  • 建设游戏网站的步邹黄骅港旅游景点
  • 网站价格表简洁的网页模板
  • 徐州网站制作如何定位自己建免费网站
  • 做网站一般要多钱广州有名的广告公司
  • 信息类网站制作旅游最新资讯 新闻
  • 友情下载网站怎么做移动端网站
  • 郑州app网站开发太原网站制作网页
  • 吉林网站建设哪家有建设论坛网站自学
  • 静态网站举例无锡企业推广网站
  • 商城网站平台保险公司网站
  • 人工智能公司网站建设环球中心建于哪一年
  • 网站怎么可以被收录德阳网站制作公司
  • 东莞seo建站优化工具wordpress 大于2m的xm
  • 淘宝优惠券网站建设总代有哪些做家教网站
  • 网站制作先做数据库还是前台长春建站公司模板
  • 销售网站设计方案网页生成图片
  • 中和华丰建设有限责任公司网站网站wap版影响权重么
  • 做网站的实训报告wordpress 移动端m
  • 昆明建设网站网上宿迁官方网站
  • wordpress网站阿里云备案号小程序 wordpress
  • 网站建设运营公司推荐软文营销文章
  • 建设网站计划ppt模板宠物网站建设
  • 那个网站可以做网站测速对比设计师经常用的网站
  • 怎么建设一个淘宝客网站软件开发管理软件
  • 网站公司架构网站建设的费用计入