epoll
1. et模式:socket状态改变:缓冲区收到数据、关闭socket、,epoll_wait返回通知一次,不论是否接收完毕仅且通知一次,
例如:客户端发送数据:”hello world!“
服务端epoll_wait返回一次通知,然后recv 5个字节,接收到的是”hello“,此时缓冲区剩余数据为:“ world!”,然后继续epoll_wait,此时不会收到任何通知(即epoll_wait不会返回)
客户端如下操作会引起服务端收到通知:
a。客户端继续写数据到此socket,socket状态改变,服务端epoll_wait返回通知
b。客户端关闭socket,socket状态改变,服务端epoll_wait返回通知
服务端如下操作,epoll_wait也会产生通知
a。在数据未接收完毕时,将socket从epoll队列删除然后再添加(此时socket缓冲区必须有数据,不管是新数据还是上次未接收完毕剩余的数据)
即对socket执行删除epoll_ctl_del,添加epoll_ctl_add操作时epoll_wait会返回一次通知,这相当于socket重新加入epoll队列,而此时socket的缓冲区还保留上次接收剩余的数据,于是epoll_wait返回通知
实现步骤:
客户端:
1. 直接写”hello world!“ 12个字节
服务端关键代码:
1. 创建epoll描述符,监听socket加入epoll监听队列
epoll_fd = epoll_create(EPOLL_MAX_SOCKETS);
if(epoll_fd == -1)
{
perror("init epoll fd error");
printf("End at: %d\n", __LINE__);
exit(1);
}
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listenfd;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &ev) <0)
{
perror("epoll ctl error");
printf("End at:%d\n", __LINE__);
exit(1);
}
2. epoll等待事件发生,处理客户端连接
int flag = 0;
while(1)
{
int nfds = epoll_wait(epoll_fd, events, EPOLL_MAX_SOCKETS, 1000/*timeout*/);
if(nfds == -1)
{
perror("epoll_wait error.");
printf("End at:%d\n", __LINE__);
continue;
}
int j = 0;
int i = 0;
for(i = 0; i < nfds; ++i)
{
if(listenfd == events[i].data.fd)
{
sockConnect = accept(events[i].data.fd, (struct sockaddr*)&remoteAddr, &addrLen);
if(sockConnect == -1)
{
perror("accept error");
printf("End at:%d\n", __LINE__);
continue;
}
printf("accepted a connection, at line %d\n", __LINE__);
setnonblocking(sockConnect);
ev.events = EPOLLIN|EPOLLET; //设置ET模式
ev.data.fd = sockConnect;
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockConnect, &ev) == -1) //新socket加入epoll监听队列
{
perror("accept error");
printf("End at:%d\n", __LINE__);
return -1;
}
}
else
{
{
int r=0;
pthread_t tid = 0;
r = pthread_create(&tid, NULL, recv_thread, (void *)&events[i].data.fd); //创建线程处理socket
}
}
}
3. 接收线程函数处理接收到的socket
void *recv_thread(void *data)
{
int sock = *(int*)data;
char buf[1024];
struct epoll_event ev;
memset(buf, 0, sizeof(buf));
/* while(1)
{*/
int r = recv(sock, buf, 5, 0); //仅接收5个字节”hello“, 剩余” world!“,此时客户端再发送任意字节数据或者关闭socket,就会触发读事件,再次执行该行时就会返回剩余数据的前5字节” worl“。
if(r == -1)
{
//perror("recv error");
if(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
{
printf("recv error: %d, recv_thread exited. errno: %d, errmsg:%s\n", r, errno, strerror(errno));
sleep(1);
continue;
}
else
{
printf("recv error: %d, recv_thread exited. errno: %d, errmsg:%s\n", r, errno, strerror(errno));
break;
}
}
if(r == 0)
{
close(sock);
printf("socket colsed: %d, recv_thread exited.\n", r);
return NULL;
}
buf[r] = '\0';
if(r != 0)
printf("recv_thread recved:%s\n", buf);
/*break;
}*/
}