user-0 提问: Linux 中用select 机制实现多个客户端并发在线问题

回复:
user: user-1
content: 老赵，你那个select对吗？我觉得有点问题

user: user-2
content: 可以是可以，有点儿蹩脚了先创建监听的fd，和连接fd列表（里面还有记录已经写了多少了），然后循环开始【将监听fd和所有连接fd加入集合，然后开始select，返回后先判断是否有新的连接，如果有就accept，将产生的连接fd加入连接列表，遍历所有连接fd，每个fd写1k数据，如果已经写完了，则关闭这个连接，从连接fd列表里删除】大概是这样吧

user: user-0
content: 大概是这样，不过要同时遍历读写的fd集合才能实现

user: user-2
content: 嗯对是要区分读写，赵老师的框架就可以，改成linux的就行

user: user-3
content: 仅供参考：（尽管是windows下的例子） /******************************************************************************\ * ioctl.c - TCP server * *       This is a part of the Microsoft Source Code Samples. *       Copyright 1996-1997 Microsoft Corporation. *       All rights reserved. *       This source code is only intended as a supplement to *       Microsoft Development Tools and/or WinHelp documentation. *       See these sources for detailed information regarding the *       Microsoft samples programs. \******************************************************************************/    #define WIN32_LEAN_AND_MEAN #include <winsock2.h> #include <stdlib.h> #include <stdio.h> #include <string.h>  #define DEFAULT_PORT 5001  int ReadAndEcho(SOCKET , char *,int ) ; int WriteMessage(SOCKET , char *,int ) ;  void Usage(char *progname) {     fprintf(stderr,"Usage\n%s -e [endpoint] -i [interface]\n",         progname);     fprintf(stderr,"Where:\n");     fprintf(stderr,"\tendpoint is the port to listen on\n");     fprintf(stderr,"\tinterface is the ipaddr (in dotted decimal notation)");     fprintf(stderr," to bind to\n");     fprintf(stderr,"Defaults are 5001 and INADDR_ANY\n");     WSACleanup();     exit(1); } int main(int argc, char **argv) {      char Buffer[128];     char *interface= NULL;     unsigned short port=DEFAULT_PORT;     int    fromlen;     int    i, ioctl_opt =1;     struct sockaddr_in local, from;     WSADATA    wsaData;     SOCKET listen_socket, msgsock;     fd_set readfds,    writefds, exceptfds;      /* Parse arguments */     if (argc >1) {         for(i=1;i <argc;i++) {             if ( (argv[i][0] ==    '-') || (argv[i][0] == '/') ) {                 switch(tolower(argv[i][1]))    {                     case 'i':                         interface =    argv[++i];                         break;                     case 'e':                         port = atoi(argv[++i]);                         break;                     default:                         Usage(argv[0]);                         break;                 }             }             else                 Usage(argv[0]);         }     }          if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {         fprintf(stderr,"WSAStartup failed with error %d\n",WSAGetLastError());         WSACleanup();         return -1;     }          if (port == 0){         Usage(argv[0]);     }      //     // The fd sets should be zeroed out before using them to prevent errors.     FD_ZERO(&readfds);     FD_ZERO(&writefds);     FD_ZERO(&exceptfds);     memset(Buffer,0,sizeof(Buffer));      local.sin_family = AF_INET;      //     // bind to specific interface if desired.      local.sin_addr.s_addr = (!interface)?INADDR_ANY:inet_addr(interface);      /*      * Port MUST be in Network Byte Order      */     local.sin_port = htons(port);      listen_socket = socket(AF_INET, SOCK_STREAM,0); // TCP socket     if (listen_socket == INVALID_SOCKET){         fprintf(stderr,"socket() failed with error %d\n",WSAGetLastError());         WSACleanup();         return -1;     }     //     // bind() associates a local address and port combination with the     // socket just created.      if (bind(listen_socket,(struct sockaddr*)&local,sizeof(local) )         == SOCKET_ERROR) {         fprintf(stderr,"bind() failed with error %d\n",WSAGetLastError());         WSACleanup();         return -1;     }      //     // start listening on the socket for incoming connections     //     if (listen(listen_socket,5) == SOCKET_ERROR) {         fprintf(stderr,"listen() failed with error %d\n",WSAGetLastError());         WSACleanup();         return -1;     }     printf("%s: Listening on port %d\n",argv[0],port);      //     // Set the socket to non-blocking mode.     //     if (ioctlsocket(listen_socket,FIONBIO,&ioctl_opt) == SOCKET_ERROR) {         fprintf(stderr,"ioctlsocket failed %d\n",WSAGetLastError());         WSACleanup();         return -1;     }     //     // The structure of    the    loop below is very simple. We only accept one     // connection at a time. As    soon as    another    client connects, we     // disconnect the first    one, and start talking to the new client.     // All this    server does    is to echo the data    received on    the    socket     // back    to the client.     //     // This    is not a very realistic    server,    but    it does    serve to show that     // select()    does not scale very    well on    win32. If we were dealing     // with    more than one client, we would have    to have    a list of sockets     // that    are    in each    fdset to be    able to    check them when    select()     // returns.     //     while(1) {          //         // A socket    in the listen()    state becomes ready    to read    when a         // client connects to it. An accept() will complete    without         // blocking.         // Since select    sets the sockets that are ready    to be read from    or         // written to, we have to include listen_socket    in the fdset each time         // through the loop.         //          FD_SET(listen_socket,&readfds);          i =    select(0,&readfds,&writefds,&exceptfds,NULL);         if (i == SOCKET_ERROR) {             fprintf(stderr,"select failed %d\n",WSAGetLastError());         }         if (i==0){             fprintf(stderr,"Select returned no fds ready\n");         }          if (FD_ISSET(listen_socket,    &readfds)){             //             // close the previous client socket.             // We must also    clear it from the fdset    to prevent select()             // from    failing.             //             closesocket(msgsock);             FD_CLR(msgsock,&readfds);             FD_CLR(msgsock,&writefds);             fromlen    = sizeof(from);             msgsock= accept(listen_socket,(struct sockaddr*)&from,&fromlen);             if (msgsock    == INVALID_SOCKET) {                 fprintf(stderr,"accept failed %d\n",WSAGetLastError());                 WSACleanup();                 return -1;             }             FD_SET(msgsock,&writefds);             FD_SET(msgsock,&readfds);             continue;         }         if (FD_ISSET(msgsock,&readfds) ) {             //             // socket is ready to read,    i.e., there    is data    on the socket.             //             if (ReadAndEcho(msgsock,Buffer,sizeof(Buffer))<0) {                 fprintf(stderr,"terminating connection\n");                 FD_CLR(msgsock,&readfds);                 FD_CLR(msgsock,&writefds);                 closesocket(msgsock);                 continue;             }         }         if (FD_ISSET(msgsock,&writefds)    ){             if (WriteMessage(msgsock,Buffer,sizeof(Buffer))    <=0) {                 fprintf(stderr,"terminating connection\n");                 FD_CLR(msgsock,&readfds);                 FD_CLR(msgsock,&writefds);                 closesocket(msgsock);                 continue;             }         }         FD_SET(msgsock,&writefds);         FD_SET(msgsock,&readfds);     } } int ReadAndEcho(SOCKET insock, char *Buffer,int size) {     int    rc;      rc = recv(insock,Buffer,size,0);      if (rc == SOCKET_ERROR)    {         fprintf(stderr,"recv() failed with error %d\n",WSAGetLastError());           return -1;     }     if (rc ==0)    {         fprintf(stderr,"Connection closed by client\n");         return 0;     }     printf("Received [%s] from client\n",Buffer);     return rc; } int WriteMessage(SOCKET outsock, char *Buffer,int size) {     int    rc;     int    lasterr;      printf("Sending [%s] to client\n",Buffer);     rc = send(outsock,Buffer,size, 0);      if (rc == SOCKET_ERROR)    {       lasterr =    WSAGetLastError();       if (lasterr == WSAEWOULDBLOCK)         return 0;       else {         fprintf(stderr,"send() failed with error %d\n",lasterr);             return -1;       }     }     if (rc ==0)    {         fprintf(stderr,"Connection closed by client\n");     }     return rc; }

user: user-0
content: 谢谢，后来同时使用读写文件描述符集合实现了

user: user-3
content: 多进程同时下载不香吗？

user: user-0
content: 导师给的一个任务，要求用select呢

user: user-4
content: 不起新线程是不可能的cpu是流水线作业，就算多核，也是按顺序执行你的代码代码里规定了流程，cpu就会完全按照你给定的流程来输出结果

user: user-0
content: 是的，但是我可以通过降低阻塞时间来使两个都在输出，比如我下载两个文件，a下载1024byte，b下载1024byte，循环这样，本质上可能不是并发，但从客户端看来是不是在同时下载呢？

user: user-4
content: 哦，是的，这样是可以的，遍历句柄集合，每个发1k，可以的。accept设置为非阻塞模式，每次accept到之后遍历select集合，所有可写的都写1k数据（或者自己做一些管理，想着都烦）,但是对链接状态的管理也挺麻烦，之前好像看过有相关的文章。4老师的代码，你先看看能不能用吧

user: user-5
content: 收到可写信号后新起线程

user: user-0
content: 你好，能否不使用线程做到呢，仅使用i/o复用

user: user-5
content: 对多线程这么排斥？参考一下libevent，muduo等高效的网络通讯框架，直接用就行了