`
zhaohaolin
  • 浏览: 1012168 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【分享】Linux下各类TCP网络服务器的实现源代码之一

 
阅读更多

Linux下各类TCP网络服务器 的实现源代码

大家都知道各类网络服务器程序的编写步骤,并且都知道网络服务器就两大类:循环服务和并发服务。这里附上源代码来个小结吧。

首先,循环网络服务器编程实现的步骤是这样的:

这种服务器模型是典型循环服务,如果不加上多进程/线程技术 ,此种服务吞吐量有限,大家都可以看到,如果前一个连接服务数据没有收发完毕后面的连接没办法处理。所以一般有多进程技术,对一个新连接启用一个新进程去处理,而监听socket继续监听。

/************关于本文档********************************************
*filename: Linux下各类TCP网络服务器的实现源代码
*purpose: 记录Linux下各类tcp服务程序源代码
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com )
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2006-07-04 22:00:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
*********************************************************************/

一个循环TCP服务源代码(因为用fork进行多进程服务了,所以这种服务现实中也有用)如下:

  1. /*----------------------源代码开始--------------------------------------------*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <netinet/in.h>
  8. #include <sys/socket.h>
  9. #include <sys/wait.h>
  10. /*********************************************************************
  11. *filename: cycletcpserver.c
  12. *purpose: 循环tcp服务端程序
  13. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  14. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  15. *date time:2006-07-04 22:00:00
  16. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  17. * 但请遵循GPL
  18. *Thanks to: Google.com
  19. *********************************************************************/
  20. int main(int argc, char ** argv)
  21. {
  22.     int sockfd,new_fd; /* 监听socket: sock_fd,数据传输socket: new_fd */
  23.     struct sockaddr_in my_addr; /* 本机地址信息 */
  24.     struct sockaddr_in their_addr; /* 客户地址信息 */
  25.     unsigned int sin_size, myport, lisnum;

  26.     if(argv[1])  myport = atoi(argv[1]);
  27.     else myport = 7838;

  28.     if(argv[2])  lisnum = atoi(argv[2]);
  29.     else lisnum = 2;

  30.     if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  31.         perror("socket");
  32.         exit(1);
  33.     }
  34.     my_addr.sin_family=PF_INET;
  35.     my_addr.sin_port=htons(myport);
  36.     my_addr.sin_addr.s_addr = INADDR_ANY;
  37.     bzero(&(my_addr.sin_zero), 0);
  38.     if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
  39.         perror("bind");
  40.         exit(1);
  41.     }

  42.     if (listen(sockfd, lisnum) == -1) {
  43.         perror("listen");
  44.         exit(1);
  45.     }
  46.     while(1) {
  47.         sin_size = sizeof(struct sockaddr_in);
  48.         if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
  49.             perror("accept");
  50.             continue;
  51.         }
  52.         printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
  53.         if (!fork()) { /* 子进程代码段 */
  54.             if (send(new_fd, "Hello, world!\n", 14, 0) == -1) {
  55.                 perror("send");
  56.                 close(new_fd);
  57.                 exit(0);
  58.             }
  59.         }
  60.         close(new_fd); /*父进程不再需要该socket*/
  61.         waitpid(-1,NULL,WNOHANG);/*等待子进程结束,清除子进程所占用资源*/
  62.     }
  63. }
  64. /*----------------------源代码结束--------------------------------------------*/
复制代码


一个测试客户端代码如下:

  1. /*----------------------源代码开始--------------------------------------------*/
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <netdb.h>
  7. #include <sys/types.h>
  8. #include <netinet/in.h>
  9. #include <sys/socket.h>
  10. #define MAXDATASIZE 100 /*每次最大数据传输量 */
  11. /*********************************************************************
  12. *filename: cycletcpclient.c
  13. *purpose: 循环tcp客户端程序
  14. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  15. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  16. *date time:2006-07-04 22:20:00
  17. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  18. * 但请遵循GPL
  19. *Thanks to: Google.com
  20. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  21. *********************************************************************/

  22. int main(int argc, char *argv[])
  23. {
  24.     int sockfd, numbytes;
  25.     char buf[MAXDATASIZE];
  26.     struct hostent *he;
  27.     struct sockaddr_in their_addr;
  28.     unsigned int myport;

  29.     if(argv[2]) myport = atoi(argv[2]);
  30.     else myport = 7838;

  31.     if (argc != 3) {
  32.         fprintf(stderr,"usage: %s hostname port\n", argv[0]);
  33.         exit(1);
  34.     }
  35.     if((he=gethostbyname(argv[1]))==NULL) {
  36.         herror("gethostbyname");
  37.         exit(1);
  38.     }
  39.     if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  40.         perror("socket");
  41.         exit(1);
  42.     }
  43.     their_addr.sin_family=PF_INET;
  44.     their_addr.sin_port=htons(myport);
  45.     their_addr.sin_addr = *((struct in_addr *)he->h_addr);
  46.     bzero(&(their_addr.sin_zero),0);
  47.     if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
  48.         perror("connect");
  49.         exit(1);
  50.     }
  51.     if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
  52.         perror("recv");
  53.         exit(1);
  54.     }
  55.     buf[numbytes] = 0;
  56.     printf("Received: %s\n",buf);
  57.     close(sockfd);
  58.     return 0;
  59. }
  60. /*----------------------源代码结束--------------------------------------------*/
复制代码


gcc cycletcpserver.c -o tcpserver和gcc cycletcpclient.c -o tcpclient分别编译上述代码后运行情况如下:
服务端运行显示:

administrator@ubuzlf:/data/example/c$ ./tcpserver
server: got connection from 127.0.0.1
server: got connection from 127.0.0.1
server: got connection from 127.0.0.1


客户端运行显示:

administrator@ubuzlf:/data/example/c$ ./tcpclient 127.0.0.1 7838
Received: Hello, world!

administrator@ubuzlf:/data/example/c$ ./tcpclient 127.0.0.1 7838
Received: Hello, world!

administrator@ubuzlf:/data/example/c$ ./tcpclient 127.0.0.1 7838
Received: Hello, world!


不得不说的一个概念性问题:阻塞与非阻塞
在阻塞服务中,当服务器运行到accept语句而没有客户连接服务请求到来,那么会发生什么情况? 这时服务器就会停止在accept语句上等待连接服务请求的到来;同样,当程序运行到接收数据语句recv时,如果没有数据可以读取,则程序同样会停止在 接收语句上。这种情况称为阻塞(blocking)。
但如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接;否则就继续做其他事情,则可以通过将 socket设置为非阻塞方式来实现:非阻塞socket在没有客户在等待时就使accept调用立即返回 。
通过设置socket为非阻塞方式,可以实现“轮询”若干socket。当企图从一个没有数据等待处理的非阻塞socket读入数据时,函数将立即返回, 并且返回值置为-1,并且errno置为EWOULDBLOCK。但是这种“轮询”会使CPU处于忙等待方式,从而降低性能。考虑到这种情况,假设你希望 服务器监听连接服务请求的同时从已经建立的连接读取数据,你也许会想到用一个accept语句和多个recv()语句,但是由于accept及recv都 是会阻塞的,所以这个想法显然不会成功。
调用非阻塞的socket会大大地浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的 一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程 选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销。

其次,并发服务器,在上述cycletcpserver.c中,由于使用了fork技术也可以称之为并发服务器,但这种服务器并不是真正意义上的IO多路复用的并发服务器,并且由于没有处理阻塞问题,实际应用有各种各样的问题。

一个典型IO多路复用的单进程并发服务器流程如下:
/*IO多路复用并发服务流程图*/

下面是一个演示IO多路复用的源程序,是一个端口转发程序,但它的用处相当大,实际应用中的各类代理软件 或端口映射软件都是基于这样的代码的,比如Windows下的WinGate、WinProxy等都是在此基础上实现的。源代码如下:

  1. /*----------------------源代码开始--------------------------------------------*/
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <unistd.h>
  5. #include <sys/time.h>
  6. #include <sys/types.h>
  7. #include <string.h>
  8. #include <signal.h>
  9. #include <sys/socket.h>
  10. #include <netinet/in.h>
  11. #include <arpa/inet.h>
  12. #include <errno.h>

  13. static int forward_port;

  14. #undef max
  15. #define max(x,y) ((x) > (y) ? (x) : (y))

  16. /*************************关于本文档************************************
  17. *filename: tcpforwardport.c
  18. *purpose: 演示了select的用法,这是一个极好的代理软件核心,专门作端口映射用
  19. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  20. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  21. *date time:2006-07-05 19:00:00
  22. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  23. * 但请遵循GPL
  24. *Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码
  25. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  26. *********************************************************************/

  27. static int listen_socket (int listen_port) {
  28.     struct sockaddr_in a;
  29.     int s;
  30.     int yes;
  31.     if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
  32.         perror ("socket");
  33.         return -1;
  34.     }
  35.     yes = 1;
  36.     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) <
  37. 0) {
  38.         perror ("setsockopt");
  39.         close (s);
  40.         return -1;
  41.     }
  42.     memset (&a, 0, sizeof (a));
  43.     a.sin_port = htons (listen_port);
  44.     a.sin_family = AF_INET;
  45.     if (bind(s, (struct sockaddr *) &a, sizeof (a)) < 0) {
  46.         perror ("bind");
  47.         close (s);
  48.         return -1;
  49.     }
  50.     printf ("accepting connections on port %d\n", (int) listen_port);
  51.     listen (s, 10);
  52.     return s;
  53. }

  54. static int connect_socket (int connect_port, char *address) {
  55.     struct sockaddr_in a;
  56.     int s;
  57.     if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
  58.         perror ("socket");
  59.         close (s);
  60.         return -1;
  61.     }

  62.     memset (&a, 0, sizeof (a));
  63.     a.sin_port = htons (connect_port);
  64.     a.sin_family = AF_INET;

  65.     if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) {
  66.         perror ("bad IP address format");
  67.         close (s);
  68.         return -1;
  69.     }

  70.     if (connect(s, (struct sockaddr *) &a, sizeof (a)) < 0) {
  71.         perror ("connect()");
  72.         shutdown (s, SHUT_RDWR);
  73.         close (s);
  74.         return -1;
  75.     }
  76.     return s;
  77. }

  78. #define SHUT_FD1 { \
  79.     if (fd1 >= 0) {   \
  80.         shutdown (fd1, SHUT_RDWR);  \
  81.         close (fd1);  \
  82.         fd1 = -1;     \
  83.     }   \
  84. }

  85. #define SHUT_FD2 { \
  86.     if (fd2 >= 0) {   \
  87.         shutdown (fd2, SHUT_RDWR);  \
  88.         close (fd2);  \
  89.         fd2 = -1;     \
  90.     }   \
  91. }

  92. #define BUF_SIZE 1024

  93. int main (int argc, char **argv) {
  94.     int h;
  95.     int fd1 = -1, fd2 = -1;
  96.     char buf1[BUF_SIZE], buf2[BUF_SIZE];
  97.     int buf1_avail, buf1_written;
  98.     int buf2_avail, buf2_written;

  99.     if (argc != 4) {
  100.         fprintf (stderr, "Usage\n\tfwd   \n");
  101.         exit (1);
  102.     }

  103.     signal (SIGPIPE, SIG_IGN);

  104.     forward_port = atoi (argv[2]);

  105.     /*建立监听socket*/
  106.     h = listen_socket (atoi (argv[1]));
  107.     if (h < 0) exit (1);

  108.     for (;;) {
  109.         int r, nfds = 0;
  110.         fd_set rd, wr, er;
  111.         FD_ZERO (&rd);
  112.         FD_ZERO (&wr);
  113.         FD_ZERO (&er);
  114.         FD_SET (h, &rd);

  115.         /*把监听socket和可读socket三个一起放入select的可读句柄列表里*/
  116.         nfds = max (nfds, h);
  117.         if (fd1 > 0 && buf1_avail < BUF_SIZE) {
  118.             FD_SET (fd1, &rd);
  119.             nfds = max (nfds, fd1);
  120.         }
  121.         if (fd2 > 0 && buf2_avail < BUF_SIZE) {
  122.             FD_SET (fd2, &rd);
  123.             nfds = max (nfds, fd2);
  124.         }

  125.         /*把可写socket两个一起放入select的可写句柄列表里*/
  126.         if (fd1 > 0 && buf2_avail - buf2_written > 0) {
  127.             FD_SET (fd1, &wr);
  128.             nfds = max (nfds, fd1);
  129.         }
  130.         if (fd2 > 0 && buf1_avail - buf1_written > 0) {
  131.             FD_SET (fd2, &wr);
  132.             nfds = max (nfds, fd2);
  133.         }

  134.         /*把有异常数据的socket两个一起放入select的异常句柄列表里*/
  135.         if (fd1 > 0) {
  136.             FD_SET (fd1, &er);
  137.             nfds = max (nfds, fd1);
  138.         }
  139.         if (fd2 > 0) {
  140.             FD_SET (fd2, &er);
  141.             nfds = max (nfds, fd2);
  142.         }

  143.         /*开始select*/
  144.         r = select (nfds + 1, &rd, &wr, &er, NULL);

  145.         if (r == -1 && errno == EINTR) continue;
  146.         if (r < 0) {
  147.             perror ("select()");
  148.             exit (1);
  149.         }

  150.         /*处理新连接*/
  151.         if (FD_ISSET (h, &rd)) {
  152.             unsigned int l;
  153.             struct sockaddr_in client_address;
  154.             memset (&client_address, 0, l = sizeof (client_address));
  155.             r = accept (h, (struct sockaddr *)&client_address, &l);
  156.             if (r < 0) {
  157.                 perror ("accept()");
  158.             } else {
  159.                 /*关闭原有连接,把新连接作为fd1,同时连接新的目标fd2*/
  160.                 SHUT_FD1;
  161.                 SHUT_FD2;
  162.                 buf1_avail = buf1_written = 0;
  163.                 buf2_avail = buf2_written = 0;
  164.                 fd1 = r;
  165.                 fd2 = connect_socket (forward_port, argv[3]);
  166.                 if (fd2 < 0) {
  167.                     SHUT_FD1;
  168.                 } else
  169.                     printf ("connect from %s\n", inet_ntoa(client_address.sin_addr));
  170.             }
  171.         }

  172.         /* NB: read oob data before normal reads */
  173.         if (fd1 > 0)
  174.         if (FD_ISSET (fd1, &er)) {
  175.             char c;
  176.             errno = 0;
  177.             r = recv (fd1, &c, 1, MSG_OOB);
  178.             if (r < 1) {
  179.                 SHUT_FD1;
  180.             } else
  181.                 send (fd2, &c, 1, MSG_OOB);
  182.         }

  183.         if (fd2 > 0)
  184.         if (FD_ISSET (fd2, &er)) {
  185.             char c;
  186.             errno = 0;
  187.             r = recv (fd2, &c, 1, MSG_OOB);
  188.             if (r < 1) {
  189.                 SHUT_FD1;
  190.             } else
  191.                 send (fd1, &c, 1, MSG_OOB);
  192.         }

  193.         /* NB: read data from fd1 */
  194.         if (fd1 > 0)
  195.         if (FD_ISSET (fd1, &rd)) {
  196.             r = read (fd1, buf1 + buf1_avail, BUF_SIZE - buf1_avail);
  197.             if (r < 1) {
  198.                 SHUT_FD1;
  199.             } else
  200.                 buf1_avail += r;
  201.         }

  202.         /* NB: read data from fd2 */
  203.         if (fd2 > 0)
  204.         if (FD_ISSET (fd2, &rd)) {
  205.             r = read (fd2, buf2 + buf2_avail, BUF_SIZE - buf2_avail);
  206.             if (r < 1) {
  207.                 SHUT_FD2;
  208.             } else
  209.                 buf2_avail += r;
  210.         }

  211.         /* NB: write data to fd1 */
  212.         if (fd1 > 0)
  213.         if (FD_ISSET (fd1, &wr)) {
  214.             r = write (fd1, buf2 + buf2_written, buf2_avail - buf2_written);
  215.             if (r < 1) {
  216.                 SHUT_FD1;
  217.             } else
  218.                 buf2_written += r;
  219.         }

  220.         /* NB: write data to fd1 */
  221.         if (fd2 > 0)
  222.         if (FD_ISSET (fd2, &wr)) {
  223.             r = write (fd2, buf1 + buf1_written, buf1_avail - buf1_written);
  224.             if (r < 1) {
  225.                 SHUT_FD2;
  226.             } else
  227.                 buf1_written += r;
  228.         }

  229.         /* check if write data has caught read data */
  230.         if (buf1_written == buf1_avail) buf1_written = buf1_avail = 0;
  231.         if (buf2_written == buf2_avail) buf2_written = buf2_avail = 0;

  232.         /* one side has closed the connection, keep writing to the other side until empty */
  233.         if (fd1 < 0 && buf1_avail - buf1_written == 0) {
  234.             SHUT_FD2;
  235.         }
  236.         if (fd2 < 0 && buf2_avail - buf2_written == 0) {
  237.             SHUT_FD1;
  238.         }
  239.     }
  240.     return 0;
  241. }
  242. /*----------------------源代码结束--------------------------------------------*/
复制代码
用gcc tcpforwardport.c -o MyProxy编译此程序后运行效果如下:
./MyProxy 8000 80 172.16.100.218
accepting connections on port 8000
connect from 127.0.0.1

当有用户访问本机的8000端口时,MyProxy程序将把此请求转发到172.16.100.218主机的80端口,即实现了一个http代理。

关于select函数:
其函数原型为:
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
此函数的功能是由内核检测在timeout时间内,是否有readfds,writefds,exceptfds三个句柄集(file descriptors)里的某个句柄(file descriptor)的状态符合寻求,即readfds句柄集里有句柄可读或writefds句柄集里有可写或exceptfds句柄集里有例外发生, 任何一个有变化函数就立即返回,返回值为timeout发生状态变化的句柄个数。
n是所有readfds,writefds,exceptfds三个句柄集(file descriptors)里编号最大值加1。比如:要检测两个socket句柄fd1和fd2在timeout时间内是否分别可读和可写就可以这样:
先把两个句柄集(file descriptors)清零:
        FD_ZERO (&readfds);
        FD_ZERO (&writefds);
然后把fd1加入读检测集:
        FD_SET (fd1, &readfds);
然后把fd2加入写检测集:
        FD_SET (fd2, &writefds);
再给timeout设置值,timeout是这样的一个结构:
              struct timeval {
                  long    tv_sec;         /* seconds */
                  long    tv_usec;        /* microseconds */
              };
你可以这样赋值:
        timeout.tv_sec=1;
        timeout.tv_uec=0;
表示检测在1秒钟内是否有句柄状态发生变化。
如果有句柄发生变化,就可以用FD_ISSET检测各个句柄,比如:
                FD_ISSET (fd1, &readfds);//检测是否fd1变成可读的了
                FD_ISSET (fd2, &writefds);//检测是否fd2变成可写的了
示意程序代码如下:
  1. /*----------------------示意代码开始--------------------------------------------*/
  2.     fd1 = socket();//创建一个socket
  3.     fd2 = socket();//创建一个socket
  4.     while(1)  {
  5.         FD_ZERO (&readfds);
  6.         FD_ZERO (&writefds);
  7.         FD_SET (fd1, &readfds);
  8.         FD_SET (fd2, &writefds);
  9.         timeout.tv_sec=1;
  10.         timeout.tv_uec=0;
  11.         ret = select(fd1>fd2?(fd1+1):(fd2+1), &readfds, &writefds, NULL, &timeout);
  12.         if(ret < 0) {printf("系统错误,select出错,错误代码:%d, 错误信息:%s", errno, strerror(errno));}
  13.         else if(ret == 0) {printf("select超时返回,没有任何句柄状态发生变化!");}
  14.         //有句柄状态发生了变化
  15.         if(FD_ISSET(fd1, &readfds)) {
  16.             fd1有数据可读;
  17.             fd1里的数据被读出来;
  18.         }
  19.         if(FD_ISSET(fd2, &writefds)) {
  20.             fd2可写;
  21.             fd2里发送数据给对方;
  22.         }
  23.     }
  24. /*----------------------示意代码结束--------------------------------------------*/
复制代码

经常用到的几个自定义函数:
1、开启监听的函数
  1. /*----------------------源代码代码开始--------------------------------------------*/
  2. int
  3. OpenSCPServer(int port, int total, int sendbuflen, int recvbuflen, int blockORnot, int reuseORnot)    {
  4. /*************************关于本函数************************************
  5. *function_name: OpenSCPServer
  6. *参数说明:port整数型监听端口号,total整数型监听个数,sendbuflen整数型发送缓冲区大小
  7. *          recvbuflen整数型接收缓冲区大小,blockORnot整数型是否阻塞,reuseORnot整数型是否端口重用
  8. *purpose: 用来建立一个tcp服务端socket
  9. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  10. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  11. *date time:2006-07-05 20:00:00
  12. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  13. * 但请遵循GPL
  14. *Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码
  15. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  16. *Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含GetCurrentTime.h头文件
  17. *********************************************************************/
  18.     int    sockfd = 0, ret = 0, opt = 0, flags=1;
  19.     struct sockaddr_in    laddr;

  20.     ret = sockfd = socket(PF_INET, SOCK_STREAM, 0);
  21.     if(ret < 0)    {
  22.         sprintf(errorMessage, "OpenTCPServer socket() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  23.         return -1;
  24.     }

  25.     ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseORnot, sizeof(int));
  26.     if(ret < 0)    {
  27.         sprintf(errorMessage, "OpenTCPServer setsockopt() reuse error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  28.         return -2;
  29.     }

  30.     ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuflen, sizeof(int));
  31.     if ( ret < 0)    {
  32.         sprintf(errorMessage, "OpenTCPServer setsockopt() recvbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  33.         return -3;
  34.     }

  35.     ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuflen, sizeof(int));
  36.     if (ret < 0)    {
  37.         sprintf(errorMessage, "OpenTCPServer setsockopt() sendbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  38.         return -4;
  39.     }

  40.     ioctl(sockfd,FIONBIO,&blockORnot);/*block or not*/

  41.     laddr.sin_family = PF_INET;
  42.     laddr.sin_port = htons(port);
  43.     laddr.sin_addr.s_addr = INADDR_ANY;
  44.     bzero(&(laddr.sin_zero), 8);

  45.     ret = bind(sockfd, (struct sockaddr *)&laddr, sizeof(struct sockaddr));
  46.     if(ret < 0)    {
  47.         sprintf(errorMessage, "OpenTCPServer bind() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  48.         close(sockfd);
  49.         return -5;
  50.     }
  51.     ret = listen(sockfd, total);
  52.     if(ret < 0)    {
  53.         sprintf(errorMessage, "OpenTCPServer listen() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));
  54.         close(sockfd);
  55.         return -6;
  56.     }
  57.     sprintf(errorMessage, "OpenTCPServer opened on port.%d(%d) OK, socket(%d), buf(%d:%d)! %s", port, total, sockfd, sendbuflen, recvbuflen, GetCurrentTime(0, 0));
  58.     return sockfd;
  59. }
  60. /*----------------------源代码代码结束--------------------------------------------*/
复制代码

2、连接服务器的函数
  1. /*----------------------源代码代码开始--------------------------------------------*/
  2. int
  3. ConnectSCPServer(char * serverip, int serverport, int blockORnot)    {
  4. /*************************关于本函数************************************
  5. *function_name: ConnectSCPServer
  6. *参数说明:serverip服务器IP地址或主机名,serverport服务器端口,blockORnot整数型是否阻塞
  7. *purpose: 用来建立一个tcp客户端socket
  8. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  9. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  10. *date time:2006-07-05 20:40:00
  11. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  12. * 但请遵循GPL
  13. *Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码
  14. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  15. *Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件
  16. *********************************************************************/
  17.     int    serversock = 0, ret = 0;
  18.     unsigned long    addr;
  19.     struct sockaddr_in    sin;
  20.     struct hostent *he;

  21.     if((he=gethostbyname(serverip))== 0) {
  22.         sprintf(errorMessage, "ConnectSCPServer IP address '%s' error! return:-1 %s", serverip, GetCurrentTime(0, 0));
  23.         return -1;
  24.     }

  25.     serversock = socket(PF_INET, SOCK_STREAM, 0);
  26.     if(serversock == -1)    {
  27.         sprintf(errorMessage, "ConnectSCPServer socket() error! return:-2, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));
  28.         return -2;
  29.     }

  30.     ioctl(serversock, FIONBIO, &blockORnot);  //block or not

  31.     memset((char*)&sin, 0, sizeof(struct sockaddr_in));
  32.     sin.sin_family = PF_INET;
  33.     sin.sin_port = htons(serverport);
  34.     sin.sin_addr = *((struct in_addr *)he->h_addr);

  35.     ret = connect(serversock, (struct sockaddr *)&sin, sizeof(sin));

  36.     if(ret == -1)    {
  37.         sprintf(errorMessage, "ConnectSCPServer connect() error! return:-3, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));
  38.         close(serversock);
  39.         return -3;
  40.     }

  41.     return serversock;
  42. }
  43. /*----------------------源代码代码结束--------------------------------------------*/
复制代码

3、发送数据函数Send
  1. /*----------------------源代码代码开始--------------------------------------------*/
  2. int
  3. Send(int sock, char * buf, size_t size, int flag, int timeout)    {
  4. /*************************关于本函数************************************
  5. *function_name: Send
  6. *参数说明:sock整数型socket,buf待发送的内容,size要发送的大小,flag发送选项,timeout超时时间值
  7. *purpose: 用来通过一个socket在指定时间内发送数据
  8. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  9. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  10. *date time:2006-07-05 20:58:00
  11. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  12. * 但请遵循GPL
  13. *Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码
  14. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  15. *Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件
  16. *********************************************************************/
  17.     int i = 0, ret = 0, intretry = 0;

  18.     struct timeval tival;
  19.     fd_set writefds;
  20.     int maxfds = 0;

  21.     tival.tv_sec = timeout;
  22.     tival.tv_usec = 0;

  23.     FD_ZERO(&writefds);

  24.     if(sock > 0) {
  25.         FD_SET(sock, &writefds);
  26.         maxfds=((sock > maxfds)?sock:maxfds);
  27.     }
  28.     else    {
  29.         sprintf(errorMessage, "Send socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));
  30.         return -2;
  31.     }

  32.     ret = select(maxfds + 1, NULL, &writefds, NULL, &tival);
  33.     if(ret <= 0) {
  34.         if(ret < 0)    sprintf(errorMessage, "Send socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
  35.         else sprintf(errorMessage, "Send socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));
  36.         close(sock);
  37.         return -3;
  38.     }
  39.     if(!(FD_ISSET(sock, &writefds)))    {
  40.         sprintf(errorMessage, "Send socket:%d not in writefds! %s", sock, GetCurrentTime(0, 0));
  41.         close(sock);
  42.         return -4;
  43.     }

  44.     while(i < size)    {
  45.         ret = send(sock, buf + i, size - i, flag);
  46.         if(ret <= 0)    {
  47.             sprintf(errorMessage, "Send socket:%d send() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));

  48.             if (EINTR == errno)
  49.               if(intretry < 10)  {intretry++;continue;}
  50.               else sprintf(errorMessage, "Send socket:%d send() error!EINTR 10 times! %s", sock, GetCurrentTime(0, 0));

  51.             close(sock);
  52.             return -1;
  53.         }
  54.         else i += ret;
  55.     }
  56.     sprintf(errorMessage, "Send socket:%d send() OK! %d/%d bytes sent! %s", sock, i, size, GetCurrentTime(0, 0));
  57.     return i;
  58. }
  59. /*----------------------源代码代码结束--------------------------------------------*/
复制代码

4、接收数据函数Recv
  1. /*----------------------源代码代码开始--------------------------------------------*/
  2. int
  3. Recv(int sock, char * buf, size_t size, int flag, int timeout)    {
  4. /*************************关于本函数************************************
  5. *function_name: Recv
  6. *参数说明:sock整数型socket,buf接收数据的缓冲区,size要接收数据的大小,flag接收选项,timeout超时时间值
  7. *purpose: 用来从一个socket在指定时间内读取数据
  8. *tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
  9. Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
  10. *date time:2006-07-05 21:10:00
  11. *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
  12. * 但请遵循GPL
  13. *Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码
  14. *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
  15. *Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件
  16. *********************************************************************/
  17.     int i = 0, ret = 0, intretry = 0;

  18.     struct timeval tival;
  19.     fd_set readfds;
  20.     int maxfds = 0;

  21.     tival.tv_sec = timeout;
  22.     tival.tv_usec = 0;

  23.     FD_ZERO(&readfds);

  24.     if(sock > 0) {
  25.         FD_SET(sock, &readfds);
  26.         maxfds=((sock > maxfds)?sock:maxfds);
  27.     }
  28.     else    {
  29.         sprintf(errorMessage, "Recv socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));
  30.         return -2;
  31.     }

  32.     ret = select(maxfds + 1, &readfds, NULL, NULL, &tival);
  33.     if(ret <= 0) {
  34.         if(ret < 0)    sprintf(errorMessage, "Recv socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
  35.         else sprintf(errorMessage, "Recv socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));
  36.         close(sock);
  37.         return -3;
  38.     }
  39.     if(!(FD_ISSET(sock, &readfds)))    {
  40.         sprintf(errorMessage, "Recv socket:%d not in readfds! %s", sock, GetCurrentTime(0, 0));
  41.         close(sock);
  42.         return -4;
  43.     }
  44.     while(i < size)    {
  45.         ret = recv(sock, buf + i, size - i, flag);
  46.         if(ret <= 0){
  47.             sprintf(errorMessage, "Recv socket:%d recv() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));
  48.             if(errno == EINTR)   
  49.                 if(intretry < 10)  {intretry++;continue;}
  50.                 else sprintf(errorMessage, "Recv socket:%d recv() error! EINTR 10 times! %s", sock, GetCurrentTime(0, 0));
  51.             close(sock);
  52.             return -1;
  53.         }
  54.         else i += ret;
  55.     }
  56.     sprintf(errorMessage, "Recv socket:%d recv() OK! %d/%d bytes received! %s", sock, i, size, GetCurrentTime(0, 0));
  57.     return i;
  58. }
复制代码
Linux 2.6内核中提高网络I/O性能的新方法epoll



正如我昨天在“Linux下各类TCP网络服务器的实现源代码”(http://zhoulifa.bokee.com/5345930.html )一文中提到的那样,I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数。

感谢chinaunix.net上朋友safedead(http://bbs.chinaunix.net/viewpro.php?uid=407631 )提醒,我今天仔细研究了一下,证实了在2.6内核中的新的I/O技术epoll。



1、为什么select是落后的?

首先,在Linux内核中,select所用到的FD_SET是有限的,即内核中有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数,在我用的2.6.15-25-386内核中,该值是1024,搜索内核源代码得到:

include/linux/posix_types.h:#define __FD_SETSIZE        1024

也就是说,如果想要同时检测1025个句柄的可读状态是不可能用select实现的。或者同时检测1025个句柄的可写状态也是不可能的。

其次,内核中实现select是用轮询方法,即每次检测都会遍历所有FD_SET中的句柄,显然,select函数执行时间与FD_SET中的句柄个数有一个比例关系,即select要检测的句柄数越多就会越费时。

当然,在前文中我并没有提及poll方法,事实上用select的朋友一定也试过poll,我个人觉得select和poll大同小异,个人偏好于用select而已。



/************关于本文档********************************************

*filename: Linux 2.6内核中提高网络I/O性能的新方法epoll

*purpose: 补充“Linux下各类TCP网络服务器的实现源代码”一文的不足之处

*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com )

Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言

*date time:2006-07-06 22:30:00

*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

* 但请遵循GPL

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*********************************************************************/



2、2.6内核中提高I/O性能的新方法epoll



epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。要使用epoll只需要这三个系统调用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。

当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44)



以下文章转自滕昱的Web Log http://mechgouki.spaces.msn.com/blog/PersonalSpace.aspx
/*********************************引用开始******************************/

Linux2.6内核epoll介绍---我的blog 2005/3/30



[作者]:滕昱,2005/3/30,0.1版本



[版权声明]:此文档遵循GNU自由文档许可证(GNU Free Documentation License).任何人可以自由复制,分发,修改,不过如果方便,请注明出处和作者



(1)导言:



首先,我强烈建议大家阅读Richard Stevens著作《TCP/IP Illustracted Volume 1,2,3》和《UNIX Network Programming Volume 1,2》。虽然他离开我们大家已经5年多了,但是他的书依然是进入网络编程的最直接的道路。其中的3卷的《TCP/IP Illustracted》卷1是必读-如果你不了解tcp协议各个选项的详细定义,你就失去了优化程序重要的一个手段。卷2,3可以选读一下。比如卷2 讲解的是4.4BSD内核TCP/IP协议栈实现----这个版本的协议栈几乎影响了现在所有的主流os,但是因为年代久远,内容不一定那么vogue. 在这里我多推荐一本《The Linux Networking Architecture--Design and Implementation of Network Protocols in the Linux Kernel》,以2.4内核讲解Linux TCP/IP实现,相当不错.作为一个现实世界中的实现,很多时候你必须作很多权衡,这时候参考一个久经考验的系统更有实际意义。举个例子,linux内 核中sk_buff结构为了追求速度和安全,牺牲了部分内存,所以在发送TCP包的时候,无论应用层数据多大,sk_buff最小也有272的字节.



其实对于socket应用层程序来说,《UNIX Network Programming Volume 1》意义更大一点.2003年的时候,这本书出了最新的第3版本,不过主要还是修订第2版本。其中第6章《I/O Multiplexing》是最重要的。Stevens给出了网络IO的基本模型。在这里最重要的莫过于select模型和Asynchronous I/O模型.从理论上说,AIO似乎是最高效的,你的IO操作可以立即返回,然后等待os告诉你IO操作完成。但是一直以来,如何实现就没有一个完美的方 案。最著名的windows完成端口实现的AIO,实际上也是内部用线程池实现的罢了,最后的结果是IO有个线程池,你应用也需要一个线程池...... 很多文档其实已经指出了这带来的线程context-switch带来的代价。



在linux 平台上,关于网络AIO一直是改动最多的地方,2.4的年代就有很多AIO内核patch,最著名的应该算是SGI那个。但是一直到2.6内核发布,网络 模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法,在使用了NPTL的linux上面其实和windows的完成端口基本上差不多 了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()以及对Direct IO的支持(就是绕过VFS系统buffer直接写硬盘,对于流服务器在内存平稳性上有相当帮助)。



所以,剩下的select模型基本上就是我们在linux上面的唯一选择,其实,如果加上no-block socket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺 点,所以改进一直是2.4-2.5开发版本内核的任务,包括/dev/poll,realtime signal等等。最终,Davide Libenzi开发的epoll进入2.6内核成为正式的解决方案



(2)epoll的优点



<1>支持一个进程打开大数目的socket描述符(FD)



select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显 然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完 美的方案。不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。



<2>IO效率不随FD数目增加而线性下降



传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"活跃"的, 但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对"活跃"的socket进行 操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,因为这时候推动力在os内核。在一些 benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相 反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。



<3>使用mmap加速内核与用户空间的消息传递。



这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就 很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工 mmap这一步的。



<4>内核微调



这一点其实不算epoll的优点了,而是整个linux平台的优点。也许你可以怀疑linux平台,但是你无法回避linux平台赋予你微调内核的能力。 比如,内核TCP/IP协议栈使用内存池管理sk_buff结构,那么可以在运行时期动态调整这个内存pool(skb_head_pool)的大小 --- 通过echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函数的第2个参数(TCP完成3次握手 的数据包队列长度),也可以根据你平台内存大小动态调整。更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网 卡驱动架构。



(3)epoll的使用



令人高兴的是,2.6内核的epoll比其2.5开发版本的/dev/epoll简洁了许多,所以,大部分情况下,强大的东西往往是简单的。唯一有点麻烦是epoll有2种工作方式 T和ET。



LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.



ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述 符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。



epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用,具体用法请参考http://www.xmailserver.org/linux-patches/nio-improve.html

http://www.kegel.com/rn/也有一个 ... 豢淳椭廊绾问褂昧�



(4)Leader/follower模式线程pool实现,以及和epoll的配合



.....未完成,主要是要避免过多的epoll_ctl调用,以及尝试使用EPOLLONESHOT加速......



(5)benchmark



.......未完成

/*********************************引用结束******************************/


3、epoll的使用方法

这是epoll的man手册提供的一个例子,这段代码假设一个非阻塞的socket监听listener被建立并且一个epoll句柄kdpfd已经提前用epoll_create建立了:

  1.        struct epoll_event ev, *events;



  2.        for(;;) {

  3.            nfds = epoll_wait(kdpfd, events, maxevents, -1);/*wait for an I/O event. All notes here added by zhoulifa(http://zhoulifa.bokee.com) on 2006-7-6 22:10:00*/



  4.            for(n = 0; n < nfds; ++n) {

  5.                if(events[n].data.fd == listener) {/*if listen socket has an I/O, accept the new connect*/

  6.                    client = accept(listener, (struct sockaddr *) &local,

  7.                                    &addrlen);

  8.                    if(client < 0){

  9.                        perror("accept");

  10.                        continue;

  11.                    }

  12.                    setnonblocking(client);

  13.                    ev.events = EPOLLIN | EPOLLET;/*EPOLLIN-available for read*/

  14.                    ev.data.fd = client;

  15.                    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {/*add the new socket into the epoll file descriptors*/

  16.                        fprintf(stderr, "epoll set insertion error: fd=%d\n",

  17.                                client);

  18.                        return -1;

  19.                    }

  20.                }

  21.                else

  22.                    do_use_fd(events[n].data.fd);/*read from a socket which has data come*/

  23.            }

  24.        }
复制代码

4、epoll使用方法示意代码

以下代码由chinaunix.net上BBS用户safedead(http://bbs.chinaunix.net/viewpro.php?uid=407631 )提供:



  1. static int        s_epfd;//epoll描述字



  2. {//初始化epoll

  3.         struct epoll_event        ev;



  4.         //设置epoll

  5.         s_epfd = epoll_create(65535);



  6.         {//这个过程可以循环以便加入多个LISTEN套接字进入epoll事件集合

  7.                 //服务器监听创建

  8.                 rc = listen();//listen参数这里省略



  9.                 //加入epoll事件集合

  10.                 ev.events = EPOLLIN;

  11.                 ev.data.fd = rc;

  12.                 if (epoll_ctl(s_epfd, EPOLL_CTL_ADD, rc, &ev) < 0) {

  13.                         fprintf(stderr, "epoll set insertion error: fd=%d", rc);

  14.                         return(-1);

  15.                 }

  16.         }

  17. }



  18. {//epoll事件处理

  19.         int        i, nfds, sock_new;

  20.         struct epoll_event        events[16384];

  21.         for( ; ; ) {

  22.                 //等待epoll事件

  23.                 nfds = epoll_wait(s_epfd, events, 16384, -1);

  24.                 //处理epoll事件

  25.                 for(i = 0; i < nfds; i++) {

  26.                         //events[i].data.fd是epoll事件中弹出的套接字

  27.                         //接收连接

  28.                         sock_new = accept(events[i].data.fd);//accept其它参数这里省略了

  29.                         if(0 > sock_new) {

  30.                                 fprintf(stderr, "接收客户端连接失败\n");

  31.                                 continue;

  32.                         }

  33.                 }

  34.         }

  35. }
复制代码

对照safedead和前面的一份代码,我想大家一定是明白了的。



5、参考文档

Improving (network) I/O performance ...

http://www.xmailserver.org/linux-patches/nio-improve.html
下面是我自己写的cycletcpclient.c,可为什么总报错:socket operation on non_socket
请各位高手指出问题


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MAXDATASIZE 100

int main(int argc, char *argv[])
{
int sockfd;
int revNum;
unsigned int serPort;
struct sockaddr_in serAddr;
char buf[MAXDATASIZE];


if(argc!=3)
{
  fprintf(stderr,"usage: %s hostIp port\n",argv[0]);
  exit(1);
}
if(argv[2])
{
  serPort=atoi(argv[2]);
}
else
{
  serPort=7838;
}

if(sockfd=socket(PF_INET,SOCK_STREAM,0)==-1)
{
  perror("socket");
  exit(1);
}
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(serPort);
if(inet_pton(PF_INET,argv[1],&serAddr.sin_addr)<=0)
{
  perror("inet_ptoa");
  exit(1);
}
if(connect(sockfd,(struct sockaddr *)&serAddr,sizeof(struct sockaddr))==-1)
{
  perror("connect");
  exit(1);
}
if(revNum=recv(sockfd,buf,MAXDATASIZE,0)==-1)
{
  perror("receive");
  exit(1);
}
buf[revNum]=0;
printf("recvd%s\n",buf);
close(sockfd);
return 0;
}
分享到:
评论

相关推荐

    Linux下各类TCP网络服务器的实现源代码.mht

    Linux下各类TCP网络服务器的实现源代码,转之红联

    Linux下TCP网络服务器实现源代码

    本文将通过一个简单的循环TCP服务器源代码示例,探讨如何在Linux环境下构建这样的服务器。循环服务模型,也称为单线程服务模型,通常适用于处理连接请求较少或对响应时间要求不高的场景。 首先,循环TCP服务器的...

    linux下各种TCP网络服务器的实现

    TCP网络服务器实现源代码,对学习TCP网络有很大帮助

    Linux版 TCP调试工具(QT源代码)

    以上是关于"Linux版TCP调试工具(QT源代码)"项目的核心知识点,涵盖网络编程、并发处理、GUI设计等多个领域,对于提升IT专业技能具有重要价值。通过对这些知识点的深入理解和实践,可以更好地开发和优化TCP服务端模型...

    linux下多线程网络编程TCP服务器端数据传输代码

    在提供的压缩包中,"linux下多线程网络编程TCP服务器端数据传输代码"应该包含了实现这些步骤的C语言源代码。在使用前,务必阅读readme.txt文件,了解代码的运行环境、依赖库和使用方法。 这个程序可能包含以下关键...

    linux 下 通过epoll实现tcp服务器

    总之,`epoll`是Linux下实现高性能TCP服务器的关键技术,通过合理利用它可以构建出高效、稳定的网络服务程序。结合提供的源代码,我们可以深入理解`epoll`的工作机制和TCP服务器的实现细节,为实际开发提供参考。

    Linux网络编程源代码

    这份源代码是《用TCP/IP进行网际互联——客户—服务器编程与应用(Linux/POSIX套接字版)(第三卷)》一书的实践部分,通过阅读和分析这些代码,读者可以深入理解网络编程的核心概念和技巧。 在Linux系统中,网络...

    LINUX-2.4.0内核网络栈实现源代码分析

    通过深入分析这些源代码,我们可以了解Linux内核如何高效地处理网络通信,包括数据包的接收、协议解析、路由决策、拥塞控制、错误处理以及多线程和并发处理等方面的知识。这对于我们优化网络性能、调试网络问题或...

    linux tcp服务器压力测试

    在Linux环境中进行TCP服务器压力测试是一项关键的任务,它有助于评估和优化服务器的性能,确保在高并发场景下系统的稳定性和可扩展性。TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输协议,广泛...

    编写socket tcp通信,服务器是linux epoll,客户端是windows 源代码

    编写socket tcp通信,服务器是linux epoll,客户端是windows 源代码

    linux下一个web服务器源代码(多线程)

    这个“Linux下一个web服务器源代码(多线程)”提供了宝贵的资源,让你能够深入理解服务器背后的运行机制。让我们来探讨这个项目所涉及的主要知识点。 首先,**源代码**是程序员的语言,通过阅读和分析这些代码,你...

    linux网络编程-源代码.rar

    《Linux网络编程》一书是IT领域中关于操作系统与网络通信的经典教材,它深入探讨了在Linux环境下如何进行网络编程,涵盖了从低级socket接口到高级应用层协议的实现。这个压缩包“linux网络编程-源代码.rar”包含了该...

    基于linux的TCP网络聊天室设计与实现

    【基于Linux的TCP网络聊天室设计与实现】是一个项目,旨在构建一个能在多个客户端之间进行实时通信的系统,尤其强调私聊和群聊功能。在Linux操作系统环境下,TCP(传输控制协议)因其可靠性和面向连接的特性,成为...

    linux下的简单聊天室源代码(c语言实现)

    这个"linux下的简单聊天室源代码(c语言实现)"为初学者提供了一个基础的平台,了解如何在Linux上创建一个简单的多用户聊天应用。下面将详细阐述相关知识点。 1. **套接字(Sockets)编程**: 套接字是网络通信的...

    TCP.rar_LINUX TCP _linux 服务器_tcp linux_tcp 服务器

    压缩包中的"www.pudn.com.txt"可能包含了关于TCP通信的文档或教程,而"TCP"文件可能是源代码,演示了如何在Linux环境下编写TCP客户端和服务器程序。你可以通过阅读这些文件来深入了解TCP编程的具体细节,如如何处理...

    linux下telnet源代码

    总结来说,Linux下的telnet源代码是一个宝贵的教育工具,它揭示了网络通信的底层细节和Linux程序设计的技巧。无论是对TCP/IP协议的深入理解,还是对Linux系统编程的实践操作,都能从这个项目中获益匪浅。因此,对于...

    linux socket 客户端和服务器的源代码

    本篇将详细解析Linux Socket客户端和服务器的源代码,帮助理解其工作原理。 一、Socket基本概念 Socket是网络通信中的一个抽象概念,它代表了两个进程之间进行通信的端点。在Linux中,Socket分为流式(SOCK_STREAM...

    Linux系统、网络编程学习源代码

    这个名为"Linux系统、网络编程学习源代码"的压缩包文件,显然是为了帮助初学者理解和实践这两个领域的知识而设计的。 Linux系统是基于Unix的操作系统,它以其开放源码、稳定性和灵活性著称。在Linux系统中,开发者...

    QT5网络通讯TCP服务器端代码,linux和win兼容,亲测可用

    在本文中,我们将深入探讨QT5如何实现网络通信中的TCP服务器端代码,并且该代码能够在Linux和Windows操作系统上运行。 首先,TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在QT5中,...

    linux系统下基于TCP协议流媒体的简单实现(带实验报告)

    创建套接字,绑定到特定IP地址和端口,监听连接,接受连接,发送和接收数据,最后关闭套接字,是TCP服务器的基本步骤。 3. **客户端与服务器端**:在流媒体系统中,通常有一个服务器端负责接收和发送视频流,而...

Global site tag (gtag.js) - Google Analytics