socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。相关的知识,希望对你有一定的参考价值。
server:
知识点:
1)
// epoll_event 结构 events 域取值 {{{
#define EPOLLIN 0x001 // 连接上有数据可读,包括 tcp 连接关闭时收到 FIN 包
#define EPOLLPRI 0x002 // 连接上有紧急数据可读
#define EPOLLOUT 0x004 // 连接可写
#define EPOLLRDNORM 0x040 // 普通数据可读
#define EPOLLRDBAND 0x080 // 优先级数据可读
#define EPOLLWRNORM 0x100 // 普通数据可写
#define EPOLLWRBAND 0x200 // 优先级数据可写
#define EPOLLMSG 0x400 // 消息队列数据
#define EPOLLERR 0x008 // 连接上发生错误
#define EPOLLHUP 0x010 // 连接被挂起
#define EPOLLRDHUP 0x2000 // 连接远端已经关闭或半关闭
// }}}
2)
//个人感觉epoll应该是硬件的发展,导致多使用了一个事件数组,每次来清空,并插入有效事件. //而不必像原来那样,直接修改输入参数.来节省内存.这样带来编程的简便性. //原来需要每次重新设置需要触发的fd集合,因为会被改写.而现在用一个文件描述符代替,且内核不改动. //原来需要每次遍历触发的fd集合,并查看其中的数据来确定是否触发 //而现在新加了一个已触发事件集合,内存换效率吧.还有mmap技术. //网上查看了下,精华就这一段话了. //如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。 //执行epoll_create时,创建了红黑树和就绪链表, //执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上, //然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。 //执行epoll_wait时立刻返回准备就绪链表里的数据即可。 //自己分析下: //一个epoll文件描述符,存储注册的事件,依据epoll中事件的索引.内核建立一个红黑树. //红黑树保证大量socket下.中断发生时,内核得到FD,需要查看是否在我们epoll文件描述符中注册的事件上., //红黑树是效率更好的平衡查找树,log2的效率,最多大概20次的查询,就可以在百万的数据中找到fd. //如果确实是我们注册的事件,调用callback.把中断事件的信息放入到链表,再把整个链表复制给我们的struct epoll_event events[n]? //之后内核再自己清空链表? //所以基础知识很重要,大概了解红黑树和中断,就很容易理解epoll的机制和大概推算其效率. 3) //关于et和lt, //个人感觉应该说非阻塞的io不适合et,因为et只会触发一次.后续数据还没到的话,非阻塞,就会错过数据(如果之后再也没有触发过).这样的因果说法才更清晰. //所以非阻塞适合用lt,而阻塞的用lt和et应该都行.
遗留问题:
1)stdin 用epollrdnormon,会出现一运行就触发事件.要详细了解下参数.
2)要测试下 events是否内核会自动清空.打断点就可以测试.
#include <iostream> #include <sys/socket.h>//{socket_type.h}:socket,AF_INET,SOCK_STREAM,PF_INET #include <netdb.h>//{<netinet/in.h>}:IPPROTO_TCP #include <sys/errno.h> #include <string.h> #include <stdio.h>//perror #include <fcntl.h> #include <unistd.h>//close. #include <time.h> #include <arpa/inet.h>//INET_PTON #include <chrono> #include <vector> #include <algorithm> #include <poll.h> #include <sys/epoll.h> //EAGAIN、EWOULDBLOCK:请再试,说明无数据. //EINTR 指操作被中断唤醒,需要重新读/写 using namespace std; const int MAXLINE=1024*4; const int MAXFD=100; typedef struct sockaddr_in SA; string g_cmd; void Accpetthread(int serverFD); void threadProcess(int serverTempFD); int main() { //socket->addr->bind->listen->accept(read ,write) int serverFD; int intflag; g_cmd=""; SA serverAddr; bzero(&serverAddr,sizeof(serverAddr)); serverFD=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); if(serverFD==-1) { perror("create()"); return -1; } //serverAddr.sin_addr.s_addr=htonl(INADDR_ANY); inet_pton(AF_INET,"127.0.0.1",&serverAddr.sin_addr); serverAddr.sin_family=AF_INET; serverAddr.sin_port=htons(3021); //serverAddr.sin_zero?? intflag=bind(serverFD,(sockaddr*)&serverAddr,sizeof(sockaddr)); if(intflag==-1) { perror("bind()"); return -1; } listen(serverFD,10);//max queue? string cmd; cout<<"exist:input 88"<<endl; // int epollFD= epoll_create1(0); if(epollFD==-1) { perror("epoll_create()"); return -1; } struct epoll_event ev, ev2;//临时事件变量,EPOLL_CTL_ADD后,事件会被复制到到epollFD里 struct epoll_event events[100];//有棵红黑树记录数组的索引?按FD大小做key? ev.data.fd=serverFD; ev.events=EPOLLIN;//还是默认lt好. int epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,serverFD,&ev); if(epoll_ctrflag==-1) { perror("epoll_ctl()"); return -1; } ev2.data.fd=STDIN_FILENO; ev2.events=EPOLLIN; epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,STDIN_FILENO,&ev2); if(epoll_ctrflag==-1) { perror("epoll_ctl()"); return -1; } for(;;) { int epoll_wait_flag=epoll_wait(epollFD,events,100,-1); cout<<"epoll wait:"<<epoll_wait_flag<<endl; if(epoll_wait_flag==-1) { perror("wait()"); continue; } for(int i=0;i<epoll_wait_flag;++i) { if(events[i].data.fd==STDIN_FILENO) { string cmd; cin>>cmd;//这个是阻塞操作. // char buffcmd[MAXLINE]; // int cmdlen=read(STDIN_FILENO,buffcmd,MAXLINE-1); // buffcmd[cmdlen]=0; // cmd=string(buffcmd); if(cmd=="88") { close(epollFD); close(serverFD); return -1; } else { cout<<"exist:input 88"<<endl; } } else if(events[i].data.fd==serverFD) { //accpet cout<<"accpet trige"<<endl; int tempServerFD=accept(serverFD,0,0); cout<<tempServerFD<<endl; if(tempServerFD==-1) { perror("accpet"); continue; } ev.data.fd=tempServerFD; ev.events=EPOLLIN;//这里又和poll不一样,EPOLLIN,通常用作数据可读和fin信号. epoll_ctrflag=epoll_ctl(epollFD,EPOLL_CTL_ADD,tempServerFD,&ev); if(epoll_ctrflag==-1) { perror("epoll_ctl()"); continue; } } else { char readbuf[MAXLINE]; bzero(readbuf,MAXLINE); //int sizeread= read(pollfdArray[i].fd,readbuf,MAXLINE-1); int sizeread= recvfrom(events[i].data.fd,readbuf,MAXLINE-1,0,0,0); if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试? { perror("read()"); close(events[i].data.fd); ev.data.fd=events[i].data.fd; ev.events=EPOLLWRNORM; epoll_ctl(epollFD,EPOLL_CTL_DEL,events[i].data.fd,&ev); continue; } else if(sizeread==0) { close(events[i].data.fd); ev.data.fd=events[i].data.fd; ev.events=EPOLLWRNORM; epoll_ctl(epollFD,EPOLL_CTL_DEL,events[i].data.fd,&ev); continue; } else { readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号. char writebuff[MAXLINE+10]; //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘) snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf)); cout<<writebuff<<flush; write(events[i].data.fd,writebuff,strlen(writebuff)); } } } } // struct pollfd pollfdArray[MAXFD]; // pollfdArray[0].fd=STDIN_FILENO; // pollfdArray[0].events=POLLIN;//标准输入数据不是普通数据...... // pollfdArray[0].revents=0; // // pollfdArray[1].fd=serverFD; // pollfdArray[1].events=POLLRDNORM;//标准输入数据不是普通数据...... // pollfdArray[1].revents=0; // // int fdMAXIndex=1; // // for(int i=2;i<MAXFD;++i) // { // pollfdArray[i].fd=-1; // } // // while(true) // { // int pollStatus= poll(pollfdArray,fdMAXIndex+1,-1); // if(pollStatus<0) // { // perror("poll()"); // } // else if(pollStatus==0) // { // cout<<"no events. why program be here?"<<endl; // } // else // { // if(pollfdArray[1].revents==POLLRDNORM) // { // pollfdArray[1].revents=0; // int tempServerFD=accept(pollfdArray[1].fd,0,0);//-1或非负整数 // // struct timeval mytime; // mytime.tv_sec=10; // mytime.tv_usec=0; // // // setsockopt(tempServerFD,SOL_SOCKET,SO_RCVTIMEO,&mytime,sizeof(mytime)); // // // if(tempServerFD==-1) // { // perror("accept()"); // } // for(int i=2;i<MAXFD;++i) // { // if(pollfdArray[i].fd==-1) // { // pollfdArray[i].fd=tempServerFD; // pollfdArray[i].events=POLLRDNORM; // // fdMAXIndex=fdMAXIndex>i?fdMAXIndex:i; // break;// // } // // } // if(--pollStatus<=0) // { // continue; // } // } // // // for(int i=2;i<=fdMAXIndex&&pollStatus>0 ;++i) // { // if(pollfdArray[i].fd!=-1) // { // if(pollfdArray[i].revents==POLLRDNORM) // { // char readbuf[MAXLINE]; // bzero(readbuf,MAXLINE); // //int sizeread= read(pollfdArray[i].fd,readbuf,MAXLINE-1); // int sizeread= recvfrom(pollfdArray[i].fd,readbuf,MAXLINE-1,0,0,0); // if(sizeread==-1)//-1到底是个什么状态?是彻底不能连接还是可以重试? // { // perror("read()"); // close(pollfdArray[i].fd); // pollfdArray[i].fd=-1; // } // else if(sizeread==0) // { // close(pollfdArray[i].fd); // pollfdArray[i].fd=-1; // } // else // { // readbuf[sizeread]=‘\0‘;//以免溢出,插入结束符号. // char writebuff[MAXLINE+10]; // //snprintf如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0‘) // snprintf(writebuff,MAXLINE+10-1,"%s:%d\n",readbuf,strlen(readbuf)); // cout<<writebuff<<flush; // write(pollfdArray[i].fd,writebuff,strlen(writebuff)); // } // --pollStatus; // } // // if(pollfdArray[i].revents==POLLERR)//overtime? // { // cout<<"error"<<endl; // } // } // } // // // if(pollfdArray[0].revents==POLLIN) // { // pollfdArray[0].revents=0;//需要重设返回值不? // // cin>>cmd; // if(cmd=="88") // { // //close all sockets. // close(serverFD); // break; // } // } // // } // } return 0; }
以上是关于socket api- c/s模式:全双工 ;IO模式:同步阻塞,epoll,多路复用。的主要内容,如果未能解决你的问题,请参考以下文章
socket api- c/s模式:服务多次读写,客户多次写读(同步处理多客户,多线程,). IO模式:select 阻塞,多路复用《客户端select,应对服务端奔溃和利用socket的全双工发送和
socket api- c/s模式:服务写,客户读. IO模式:阻塞
socket api- c/s模式:服务读写,客户写读. IO模式:阻塞