基于linux poll模型的tcp服务器------一个服务器如何与多个客户端进行通信?

来源:互联网 发布:股票收益率数据 编辑:程序博客网 时间:2024/04/29 03:46

        在之前的博文中, 我们一起玩了Windows下的select函数, 实现了一个服务器与多个客户端进行通信。 如果一直到linux, 那几乎是大同小异的。 现在, 我们不说select, 而来接续说说linux epoll.

        在前面的博文中, 我们也介绍了linux poll检测键盘终端输入, 主要是想在网络编程之前, 对epoll的用法有个基本的了解。现在, 来玩一下基于linux poll模型的tcp服务器------一个服务器与多个客户端进行通信。

       服务端代码如下:

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <fcntl.h> #include <sys/poll.h>  #define BACKLOG 100  #if 0struct pollfd{  int fd;          //文件描述符  short events;    //请求的事件  short revents;   //返回的事件  };#endifint main()  {    int iListenSock = socket(AF_INET, SOCK_STREAM, 0);    int arrayConnSock[BACKLOG] = {0}; // 这个初始化并不好,但其实无大碍    sockaddr_in addr;      memset(&addr, 0, sizeof(addr));      inet_aton("0.0.0.0", &addr.sin_addr);      addr.sin_family = AF_INET;      addr.sin_port = htons(8888);  int iOpt = 1;    setsockopt(iListenSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt));  // 标配    bind(iListenSock, (sockaddr*)&addr, sizeof(addr));      listen(iListenSock, 100);    int flag = -1;pollfd fds[BACKLOG + 1];        // BACKLOG个通信socket和1个监听socketmemset(fds, flag, sizeof(fds)); // 设置一个标志fds[0].fd = iListenSock;        // 监听socketfds[0].events = POLLIN;int ndfs = 1;int timeoutMS = -1; // 永不超时int i = 0;for(int i = 1; i <= BACKLOG; i++){      int iRet = poll(fds, ndfs, timeoutMS);      if (iRet < 0){printf("poll error, iRet %d\n", iRet);        continue;      }if (iRet < 0){printf("poll error, iRet %d\n", iRet);        continue;      }    if (fds[0].revents & POLLIN) // fds[0]对应iListenSock, 此处表明检测到有新客户端连接过来{            arrayConnSock[i - 1] = accept(iListenSock, NULL, NULL); // arrayConnSock是通信socket            fds[i].fd = arrayConnSock[i - 1];              fds[i].events = POLLIN;ndfs++;              printf("new client came, local fd is [%d]\n", arrayConnSock[i - 1]);      }      for (int j = 0; j < BACKLOG; j++){char szBuf[1024] = {0};         if (arrayConnSock[j] > 0 && (fds[j + 1].revents & POLLIN)) // 此处检测的是已经链接的socket的读事件{              int recvLen = recv(arrayConnSock[j], szBuf, sizeof(szBuf) - 1, 0);              if (recvLen > 0){                  printf("recv data [%s] from local fd [%d] \n", szBuf, arrayConnSock[j]);              }else if(0 == recvLen)            {                close(arrayConnSock[j]);                  memset(&fds[j + 1], flag, sizeof(pollfd));                  printf("connection closed fd [%d]\n", arrayConnSock[j]);                  arrayConnSock[j] = 0;             }else{close(arrayConnSock[j]);                  memset(&fds[j + 1], flag, sizeof(pollfd));  printf("recv error, recvLen is %d", recvLen);arrayConnSock[j] = 0;}        }      }  }      close(iListenSock);      return 0;  }

        客户端代码如下:

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <fcntl.h>int main(){    int sockClient = socket(AF_INET, SOCK_STREAM, 0);    struct sockaddr_in addrSrv;    addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");    addrSrv.sin_family = AF_INET;    addrSrv.sin_port = htons(8888);    connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in));char szSendBuf[100] = "this is me";while(1){send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0);   scanf("%s", szSendBuf);}    close(sockClient);return 0;}
      makefile代码如下:

all: server clientserver: server.og++ -o server  server.oclient: client.og++ -o client  client.oserver.o: server.cppg++ -c server.cppclient.o:client.cppg++ -c client.cppclean:rm -f server client *.o
         

      编译链接后, 先启动服务端, 然后分别在同一linux机器的三个窗口上启动三个客户端, 此时, 服务端的结果为:

xxxxxx:~/network> ./servernew client came, local fd is [4]recv data [this is me] from local fd [4] new client came, local fd is [5]recv data [this is me] from local fd [5] new client came, local fd is [6]recv data [this is me] from local fd [6] 

        好了, 实现了简单的一个服务器与多个客户端进行通信。 随后, 这三个客户端可以继续与服务端进行同行, 同时, 服务端也允许更多的新客户端来连接。 当然, 客户端也可以选择断开连接(ctrl c). 多的不说, 玩一下程序就明白了, 上述poll的用法值得好好体会, 可以看到, 跟linux几乎没有太大的差别, 上述程序如果转成select模型,那也是易如反掌的。

       最后, 思考一个有意思的小问题, 为什么上述的fd是4, 5, 6 ?  



阅读全文
1 0
原创粉丝点击