核心代码,所有代码在这里下载
#include "unixnet.h" #include "chat.h" #include "sys/epoll.h" /** * 改进说明:使用epoll进行事件回调,多用户可以处于输入命令状态(不再阻塞在登陆处) */ int main (int argc, char *argv[]) { int listen_fd; socklen_t cli_len; struct sockaddr_in cli_addr,serv_addr; int ret,flags; int re_use_addr=1; char recv_buf[MAXLINE]; int i; //epoll 描述符 int efd; struct epoll_event event; struct epoll_event events[MAXUSERS]; //初始化槽位 for(i=0; i<MAXUSERS; i++) { chater[i].slot_status =SLOT_FREED ; chater[i].sock_fd =-1; chater[i].cmd .cmd_type =-1; } /*AF_INET指定ipv4,SOCK_STREAM制定流模式,0为tcp*/ listen_fd = socket(AF_INET,SOCK_STREAM,0); if(listen_fd==-1) { perror("create listen fd"); exit(1); } //服务器地址清零 bzero(&serv_addr ,sizeof(serv_addr)); serv_addr.sin_family=AF_INET; //接受所有ip的请求 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); //设置端口 serv_addr.sin_port=htons(SERVER_PORT ); setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR,(void *)&re_use_addr,sizeof(int)); //绑定描述符到端口 ret=bind(listen_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); if(ret<0) { perror("bind server port"); exit(1); } //开始监听端口请求,并设置最大的请求队列 listen(listen_fd,LISTENQ); //设置为非阻塞io if((flags=fcntl(listen_fd,F_GETFL,flags)<0)) { perror("F_SETFL error"); } //使用位域 flags|=O_NONBLOCK; if(fcntl(listen_fd,F_SETFL,flags)<0) { perror("F_SETFL error"); } //创建一个epoll句柄 efd = epoll_create(MAXUSERS); //对连接套接字设置监听事件,并设置触发模式,最后加入到epoll中进行回调 event.data.fd = listen_fd; event.events = EPOLLET| EPOLLIN; epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event); printf("listen_fd %d\n",listen_fd); //循环处理所有事件 while(1){ int n,i; //等待直到事件到来,规律性sleep,所有不会导致cpu 100% n = epoll_wait(efd, events, MAXUSERS,-1); for(i = 0;i < n;++i){ //如果是一个异常事件,则释放资源 if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { fprintf (stderr, "epoll error\n"); free_slot(findSlotIndexByConnFd(events[i].data.fd)); epoll_ctl(efd,EPOLL_CTL_DEL,events[i].data.fd,NULL); continue; //此时代表获得了一个连接建立事件,可以创建一个连接,本质上也是一个EPOLLIN }else if(listen_fd == events[i].data.fd){ int infd = -1; //accept接受连接直到失败(由于使用ET触发所以需要使用循环以保所有事件都得到处理) while((infd = accept(listen_fd,(struct sockaddr *)&cli_addr,&cli_len)) > 0){ //获得一个空槽,并将连接设置为非阻塞 get_free_slot(infd, "no_login"); event.data.fd = infd; event.events = EPOLLIN | EPOLLET; epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event); writen(infd,message[2],strlen(message [2])); printf("accept one\n"); } if(infd == -1){ printf("%d\n",errno); } //在这里处理退出事件|用户名登陆事件|正常的cmd命令 }else{ int infd = events[i].data.fd; int index = -1; char *ptr; index = findSlotIndexByConnFd(infd); //从内核读数据到用户缓冲区 ptr=&(chater[index].buffer[chater[index].next_char]); if(index != -1){ while((n=read(infd ,ptr,1))==1) { if(*ptr=='\n') { *(ptr+1)='\0'; chater[index].next_char =0; break; } //在这里丢弃过长的字符串 if(++chater[index].next_char ==MAXLINE) --chater[index].next_char ; else ++ptr; } //退出事件:当n==0代表连接丢失,当n==-1且errno!=EWOULDBLOCK代表不是因为暂时没有数据抛出错误 if(n == 0 || (n == -1&&errno!=EWOULDBLOCK)){ free_slot(findSlotIndexByConnFd(events[i].data.fd)); epoll_ctl(efd,EPOLL_CTL_DEL,events[i].data.fd,NULL); printf("user %s use slot_index %d logout\n\n",chater[index].user_id, index); continue; } //登陆用户名 if(!chater[index].user_assigned){ copyStringSkipSpace(chater[index].user_id,chater[index].buffer); chater[index].user_assigned = 1; printf("user %s use slot_index %d login\n\n",chater[index].user_id, index); //处理cmd. }else{ handle_cmd(index); } } } } } }
来源:http://www.cnblogs.com/fcat/p/5571288.html