非阻塞recv
EAGAIN、EWOULDBLOCK錯誤碼值11
返回值 | 含義 |
---|---|
>0 | 接收位元組數 |
0 | 接收FIN包,連線被對端斷開 |
-1 | (errno==EAGAIN||EWOULDBLOCK)表示還有資料未讀。反之,則表示發生了錯誤。 |
//epollServer.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <vector>
#include <time.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define MAXEVENTS 100
int InitServer(const unsigned int port);
int setnoblocking(int fd);
bool addfd(int epollfd, int fd, struct epoll_event* ev);
bool addfdET(int epollfd, int fd, struct epoll_event* ev);
int main(int argc,char** argv)
{
int listenfd=InitServer(atoi(argv[1]));
printf("listenfd=%d\n",listenfd);
if(listenfd==-1)
{
printf("InitServer failed..\n");
return -1;
}
//建立epoll控制代碼
int epollfd=epoll_create(1);
if(epollfd<0) printf("epoll_create failed..\n");
//epoll_event封裝listenfd和監視型別為有資料可讀(EPOLLIN)
struct epoll_event ev;
addfd(epollfd, listenfd, &ev);
while(true)
{
struct epoll_event events[MAXEVENTS]; //epoll_wait返回時儲存發生的事件
int infds=epoll_wait(epollfd,events,MAXEVENTS,-1);
if(infds<0)
{
printf("epoll_wait failed..\n");
break;
}
if(infds==0)
{
printf("timeout..\n");
break;
}
//發生的事件再events中從index=0依次儲存
for(int i=0;i<infds;i++)
{
//listenfd上有資料可讀事件發生
if((events[i].data.fd==listenfd) && (events[i].events&EPOLLIN))
{
struct sockaddr_in client_addr;
socklen_t client_addr_size=sizeof(client_addr);
int clientfd = accept(listenfd, (struct sockaddr*)&client_addr,&client_addr_size);
if(clientfd<0)
{
printf("accept failed..\n");
continue;
}
//連線上的客戶端新增進監視
//邊緣觸發
addfdET(epollfd,clientfd,&ev);
continue;
}
else if(events[i].events&EPOLLIN) //有資料可讀事件
{
//邊緣觸發:這段程式碼不會被epoll_wait重複觸發,所以需要手動迴圈讀取直到資料讀完。因為迴圈recv,不能設為阻塞
while(true)
{
char buf[1024];
memset(buf,0,sizeof(buf));
int isize = recv(events[i].data.fd,buf,sizeof(buf),0);
if(isize==0) //連線被對端關閉
{
printf("對端關閉連線\n");
memset(&ev,0,sizeof(struct epoll_event));
ev.data.fd=events[i].data.fd;
ev.events=EPOLLIN;
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,&ev);
close(events[i].data.fd);
printf("%ld client(%d) disconneted..\n",time(0),events[i].data.fd);
break;
}
else if(isize==-1) //資料未讀完或發生錯誤
{
if((errno==EAGAIN) || (errno==EWOULDBLOCK)) //資料未讀完
{
errno=0; //全域性變數errno被置為EAGAIN後,清空
continue; //繼續迴圈執行recv
}
else
{
//錯誤,終止recv迴圈
break;
}
}
printf("client(%d)recv msg:%s,size=%d\n",events[i].data.fd,buf,isize); //recv返回值>0
//判斷sockfd是否可寫
fd_set tmpfd;
FD_ZERO(&tmpfd);
FD_SET(events[i].data.fd,&tmpfd);
if(select(events[i].data.fd+1,0,&tmpfd,0,0)<0)
{
printf("沒有可寫資料的fd\n");
break;
}
if(write(events[i].data.fd,buf,strlen(buf))<0) //傳送
{
printf("write failed..\n");
close(events[i].data.fd);
break;
}
}
}
}
}
return 0;
}
int InitServer(const unsigned int port)
{
int sockfd=-1;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd == -1) return -1;
int opt=1;
unsigned int len=sizeof(opt);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, len);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(port);
if(bind(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) == -1)
{
close(sockfd);
sockfd=-1;
return -1;
}
if(listen(sockfd, 5) == -1)
{
close(sockfd);
sockfd=-1;
return -1;
}
return sockfd;
}
int setnoblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
{
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1)
{
return -1;
}
return 0;
}
bool addfd(int epollfd, int fd, struct epoll_event* ev)
{
memset(ev,0,sizeof(struct epoll_event));
ev->data.fd=fd;
ev->events=EPOLLIN;
//setnoblocking(fd);
return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev); //新增到epollfd
}
bool addfdET(int epollfd, int fd, struct epoll_event* ev)
{
memset(ev,0,sizeof(struct epoll_event));
ev->data.fd=fd;
ev->events=EPOLLIN|EPOLLET;
setnoblocking(fd);
return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev); //新增到epollfd
}