采用epoll实现echo server和client

服务器代码:

#include <sys/socket.h>   
#include <sys/epoll.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
#include <fcntl.h>   
#include <unistd.h>   
#include <stdio.h>   
#include <errno.h>   
#include <stdlib.h>
#include <string.h>
 
 
  
#define MAXLINE 512   
#define OPEN_MAX 100   
#define LISTENQ 20   
#define SERV_PORT   9999  
#define INFTIM 1000   
  
void setnonblocking(int sock)   
{   
      int opts;   
      opts=fcntl(sock,F_GETFL);   
  
      if(opts<0)   
      {   
            perror("fcntl(sock,GETFL)");   
            exit(1);   
      }   
  
      opts = opts | O_NONBLOCK;   
  
      if(fcntl(sock,F_SETFL,opts)<0)   
      {   
            perror("fcntl(sock,SETFL,opts)");   
            exit(1);   
      }   
}   
/*  
     假如发送端流量大于接收端的流量  
     (意思是epoll所在的程序读比转发的socket要快),  
     由于是非阻塞的socket,那么send()函数虽然返回,  
     但实际缓冲区的数据并未真正发给接收端,  
     这样不断的读和发, 
     当缓冲区满后会产生EAGAIN错误(参考man send),同时,  
     不理会这次请求发送的数据.所以,  
     需要封装socket_send()的函数用来处理这种情况,  
     该函数会尽量将数据写完再返回,返回-1表示出错。 
     在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),  
     那么会等待后再重试.这种方式并不很完美,  
     在理论上可能会长时间的阻塞在socket_send()内部,  
     但暂没有更好的办法.  
  
  */  
  
ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)  
{  
      ssize_t tmp;  
      size_t total = buflen;  
      const char *p = buffer;  
  
      while(1)  
      {  
            tmp = send(sockfd, p, total, 0);  
            if(tmp < 0)  
            {  
                  // 当send收到信号时,可以继续写,但这里返回-1.  
                  if(errno == EINTR)  
                        //return -1;zxd  
                        continue;  
  
                  // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,  
                  // 在这里做延时后再重试.  
                  if(errno == EAGAIN)  
                  {  
                        usleep(1000);  
                        continue;  
                  }  
  
                  return -1;  
            }  
  
            if((size_t)tmp == total)  
                  return buflen;  
  
            total -= tmp;  
            p += tmp;  
      }  
  
      return tmp;  
}  
  
  
void epoll_ctl_err_show()     
{     
      printf("error at epoll_ctl\n");     
      if(EBADF == errno)     
      {     
            printf("error at epoll_ctl, error   is EBADF\n");     
      }     
      else if(EEXIST == errno)     
      {     
            printf("error at epoll_ctl, error   is EEXIST\n");     
      }     
      else if(EINVAL == errno)     
      {     
            printf("error at epoll_ctl, error   is EINVAL\n");
      }     
      else if(ENOENT == errno)     
      {     
            printf("error at epoll_ctl, error   is ENOENT\n"); 
      }     
      else if(ENOMEM == errno)     
      {     
            printf("error at epoll_ctl, error   is ENOMEM\n");     
      }     
      else if(ENOSPC == errno)     
      {     
            printf("error at epoll_ctl, error   is ENOSPC\n");     
      }
        
}   
  
int main()   
{   
      int i, maxi, listenfd, connfd, sockfd, epfd, nfds;   
      ssize_t n;   
      char line[MAXLINE];   
      socklen_t clilen;   
  
      struct epoll_event ev,events[20]; //声明epoll_event结构体的变量, ev用于注册事件, events数组用于回传要处理的事件   
      epfd=epoll_create(256); //生成用于处理accept的epoll专用的文件描述符, 指定生成描述符的最大范围为256   
  
      struct sockaddr_in clientaddr;   
      struct sockaddr_in serveraddr;   
  
      listenfd = socket(AF_INET, SOCK_STREAM, 0);   
  
      setnonblocking(listenfd); //把用于监听的socket设置为非阻塞方式   
  
      ev.data.fd=listenfd; //设置与要处理的事件相关的文件描述符   
      ev.events=EPOLLIN | EPOLLET; //设置要处理的事件类型   
      epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); //注册epoll事件  
  
      bzero(&serveraddr, sizeof(serveraddr));   
      serveraddr.sin_family = AF_INET;   
      char *local_addr="127.0.0.1";   
      inet_aton(local_addr,&(serveraddr.sin_addr));  
      serveraddr.sin_port=htons(SERV_PORT);   //或者htons(SERV_PORT);   
  
      bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr));   
  
      listen(listenfd, LISTENQ);   
  
      maxi = 0;   
  
      for( ; ; ) {   
            nfds=epoll_wait(epfd, events, 20, -1); //等待epoll事件的发生   
              
			printf("looping...\n");
            for(i=0;i<nfds;++i) //处理所发生的所有事件   
            {   
                  if(events[i].data.fd==listenfd)      /**监听事件**/  
                  {   
                        //循环accept  
                        while((connfd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen)) > 0)  
                        {  
                              setnonblocking(connfd); //把客户端的socket设置为非阻塞方式  
  
                              char *str = inet_ntoa(clientaddr.sin_addr);   
                              printf("connect from %s\n", str);
  
                              ev.data.fd=connfd; //设置用于读操作的文件描述符   
                              ev.events=EPOLLIN | EPOLLET; //设置用于注测的读操作事件   
                              epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //注册ev事件   
                        }  
                  }   
                  else if(events[i].events&EPOLLIN)       /**读事件**/  
                  {   
                        fprintf(stderr, "EPOLLIN................\n");  
                        if ( (sockfd = events[i].data.fd) < 0)   
                        {  
                              continue;   
                        }  
                        memset(line, 0, MAXLINE);  
                        n = 0;  
                        int nread = 0;  
                        while((nread= read(sockfd, line + n, MAXLINE)) > 0)  
                        {  
                              n += nread;  
                        }//读到EAGAIN,说明读完了  
  
  
                        if(nread == -1 && errno != EAGAIN)  
                        {  
                              epoll_ctl_err_show();  
                              printf("[error]readline error\n");  
                              close(sockfd); //关闭一个文件描述符,那么它会从epoll集合中自动删除 
                              //描述符关闭后,后面的邋邋邋邋EPOLLOUT设置了,但不起作用了  
                              events[i].data.fd = -1;   
                        }  
  
                        //这里要加上判断,nread为0时,说明客户端已经关闭 
                        //此时,需要关闭描述符,否则在/proc/id/fd下能看到描述符会一直存在 
                        if(nread == 0)  
                        {  
							printf("[info] receive 0, close socket\n");
                            close(sockfd);  
                            continue;  
                        }  
						
						printf("receive: %s\n", line);
  
                        ev.data.fd=sockfd; //设置用于写操作的文件描述符   
                        ev.events=EPOLLOUT | EPOLLET; //设置用于注测的写操作事件   
                        epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改sockfd上要处理的事件为EPOLLOUT   
                  }   
                  else if(events[i].events & EPOLLOUT)      /**写事件**/  
                  {   
						fprintf(stderr, "EPOLLOUT................\n");  
                        sockfd = events[i].data.fd;   
                        //write(sockfd, line, n); orig  
  
                        int iRet = socket_send(sockfd, line, strlen(line) + 1);  
                        if(iRet == -1 || iRet != strlen(line) + 1)  
                        {  
                              perror("write error!");  
                        }/*zxd*/  
  
                        ev.data.fd=sockfd; //设置用于读操作的文件描述符   
                        ev.events=EPOLLIN | EPOLLET; //设置用于注册的读操作事件   
                        epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); //修改sockfd上要处理的事件为EPOLIN   
                  }   
            }   
      }   
}  

 

客户端:

#include <netinet/in.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <sys/epoll.h> 
#include <errno.h> 
#include <string.h> 
#include <fcntl.h> 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
#define BUFSIZE 512  
#define RET_ERR -1
#define RET_OK 0
#define Debug_UserLog printf
#define Debug_SysLog printf

void SetNonBlock(int fd) 
{ 
    int flag = fcntl ( fd, F_GETFL, 0 ); 
    fcntl ( fd, F_SETFL, flag | O_NONBLOCK ); 
} 
 
int main(int argc, char** argv) 
{ 
    int iRet = RET_OK; 
 
    if(4 != argc) 
    { 
        Debug_UserLog("Parameter: ServerIP Message ServerPort", RET_ERR); 
        return RET_ERR; 
    } 
 
    in_port_t i16_port = atoi(argv[3]); 
    if(0 >= i16_port) 
    { 
        Debug_UserLog("port number is wrong", RET_ERR); 
        return RET_ERR; 
    } 
 
    int sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if(-1 == sk) 
    { 
        Debug_SysLog("open socket failed!"); 
        return RET_ERR; 
    } 
 
 
    struct sockaddr_in sa = {0}; 
    sa.sin_family = AF_INET; 
    sa.sin_port = htons(i16_port); 
 
    struct sockaddr_in *psa = &sa; 
 
    iRet = inet_pton(AF_INET, argv[1], &psa->sin_addr.s_addr); 
    if(0 == iRet) 
    { 
        Debug_UserLog("inet_pton failed, invalid address!", RET_ERR); 
        close(sk); 
        return RET_ERR; 
    } 
    else if(iRet < 0) 
    { 
        Debug_SysLog("inet_pton failed"); 
        close(sk); 
        return RET_ERR; 
    } 
 
    if(connect(sk, (struct sockaddr*)&sa, sizeof(sa)) < 0) 
    { 
        Debug_SysLog("connect failed"); 
        close(sk); 
        return RET_ERR; 
    } 
 
    SetNonBlock(sk); 
 
    int efd;  
    efd = epoll_create(10);  
    if(efd == -1) 
    { 
        perror("epoll_create error!"); 
        exit(1); 
    } 
 
    struct epoll_event event; 
    struct epoll_event events[10]; 
 
    event.events = EPOLLOUT | EPOLLIN; 
    event.data.fd = sk; 
 
    epoll_ctl(efd, EPOLL_CTL_ADD, sk, &event); 
 
 
    int loop = 0; 
    while(1) 
    { 
        ssize_t numBytesRcvd = 0; 
        char buffer[BUFSIZE] = {0}; 
        int n = 0; 
        int i = 0; 
 
        if(loop == 1) 
        { 
            break; 
        } 
		
        n = epoll_wait(efd, events, 10, -1); 
		printf("looping... %d\n", n);      
 
        for(i = 0; i < n; i++) 
        { 
            if(events[i].events & EPOLLOUT) 
            { 
                printf("EPOLLOUT...............\n"); 
                snprintf(buffer, BUFSIZE, "i am process %d, just say: %s\n", getpid(), argv[2]); 
 
                int n = strlen(buffer); 
                int nsend = 0; 
 
                while(n > 0) 
                { 
                    //nsend = send(events[i].data.fd, buffer + nsend, n, 0); 
                    nsend = write(events[i].data.fd, buffer + nsend, n); 
                    if(nsend < 0 && errno != EAGAIN) 
                    { 
 
                        Debug_SysLog("send failed"); 
                        close(events[i].data.fd); 
                        return RET_ERR; 
                    } 
                    n -= nsend; 
                } 
				
				struct epoll_event evt;
				evt.events = EPOLLIN | EPOLLOUT;
				evt.data.fd = events[i].data.fd;
				epoll_ctl(efd, EPOLL_CTL_MOD, events[i].data.fd, &evt);
            } 
 
            if(events[i].events & EPOLLIN) 
            { 
                printf("EPOLLIN...............\n");
                memset(buffer, 0, BUFSIZE);
 
                int len = strlen(buffer);
                int n = 0;
                int nrecv = 0;


                while(1){
                    nrecv = read(events[i].data.fd, buffer + n, BUFSIZE - 1);
                    if(nrecv == -1 && errno != EAGAIN)
                    {
                        perror("read error!");
                    }
                    if((nrecv == -1 && errno == EAGAIN) || nrecv == 0)
                    {
                        break;
                    }
                    n += nrecv;
                }
                
				//loop = 1;
				
                printf("%s\n", buffer);
				
				struct epoll_event evt;
				evt.events = EPOLLOUT;
				evt.data.fd = events[i].data.fd;
				epoll_ctl(efd, EPOLL_CTL_MOD, events[i].data.fd, &evt);
            }
        }
		
		usleep(500*1000);
		
    }
    close(sk);
    close(efd);
    return RET_OK;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用 epoll 实现的一个简单的 TCP 客户端: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <errno.h> #define MAX_EVENTS 10 #define BUF_SIZE 1024 int main(int argc, char *argv[]) { if (argc != 3) { printf("Usage: %s <ip> <port>\n", argv[0]); exit(1); } int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(1); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(argv[1]); server_addr.sin_port = htons(atoi(argv[2])); if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); exit(1); } int epollfd = epoll_create(MAX_EVENTS); if (epollfd == -1) { perror("epoll_create"); exit(1); } struct epoll_event ev, events[MAX_EVENTS]; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { perror("epoll_ctl"); exit(1); } char buf[BUF_SIZE]; while (1) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { if (errno == EINTR) { continue; } perror("epoll_wait"); exit(1); } for (int i = 0; i < nfds; ++i) { if (events[i].data.fd == sockfd) { int n = read(sockfd, buf, BUF_SIZE); if (n == -1) { perror("read"); exit(1); } else if (n == 0) { printf("Server closed connection\n"); close(sockfd); exit(0); } else { buf[n] = '\0'; printf("Received message: %s", buf); } } } } return 0; } ``` 首先创建了一个 socket,并连接到指定的服务器。然后创建一个 epoll 实例,将 socket 加入到 epoll 中,并设置监听的事件型为可读。之后不断循环调用 epoll_wait 等待事件的发生,如果是 socket 的可读事件,则读取数据并打印。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值