要实现一个服务器程序,客户端的数量在300个左右,什么模型实现起来比较容易还能保证稳定性?
知道的给的意见。以前没有做过,如果可以的话,给点源码看看。先谢谢了....

解决方案 »

  1.   

    Select 模式下的Socket就可以.
      

  2.   

    select受FD_SETSIZE宏的影响,可能无法一次管理多大300个socket。
    还是用完成端口好,它能满足同时处理300个以上的socket且性能优越,至于复杂性,用熟了也不怕。
    参考:
    http://www.winu.cn/space-14160-do-blog-id-4154.html
      

  3.   

    IOCP并不复杂,我也是最近研究这个,后来看了之后才发现最复杂的绝对不是IOCP,而是重叠IO事件模型。
      

  4.   


    晕倒,重叠IO怎么会比IOCP复杂
      

  5.   

    用boost下的asio吧,简单易用,在windows平台下封装了完成端口,也可以设定不使用完成端口,很不错
      

  6.   

    俺就直接用ACE了
    框架那是相当的爽
      

  7.   

    随便什么模式都能处理了,才300。现在的主流CPU,小事一桩。这样低的并发连接,根本就不需要考虑什么模型。
      

  8.   

    用IOCP吧,
    虽然只有300个,但是你做一个服务器得考虑重用啊,假设下次你有机会做3000个点,那你不是又得问用什么模型呢?
    何况吃透IOCP也不是很难。
      

  9.   

    如果几百个连接的话,用完成端口有点牛刀小试了,建议使用重叠IO模式。重叠IO模式能够管理的socket连接极限大概一万左右吧,这样的话,如果将来有需求,就比较容易扩展。
    代码如下:重叠I/O模型
    Winsock2的发布使得Socket I/O有了和文件I/O统一的接口。我们可以通过使用Win32文件操纵函数ReadFile和WriteFile来进行Socket I/O。伴随而来的,用于普通文件I/O的重叠I/O模型和完成端口模型对Socket I/O也适用了。这些模型的优点是可以达到更佳的系统性能,但是实现较为复杂,里面涉及较多的C语言技巧。例如我们在完成端口模型中会经常用到所谓的“尾随数据”。1.用事件通知方式实现的重叠I/O模型
    #include <winsock2.h>
    #include <stdio.h>#define PORT    5150
    #define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")typedef struct
    {
      WSAOVERLAPPED overlap;
      WSABUF        Buffer;
      char          szMessage[MSGSIZE];
      DWORD         NumberOfBytesRecvd;
      DWORD         Flags;
    }PER_IO_OPERATION_#, *LPPER_IO_OPERATION_#;int                     g_iTotalConn = 0;
    SOCKET                  g_CliSocketArr[MAXIMUM_WAIT_OBJECTS];
    WSAEVENT                g_CliEventArr[MAXIMUM_WAIT_OBJECTS];
    LPPER_IO_OPERATION_# g_pPerIO#Arr[MAXIMUM_WAIT_OBJECTS];DWORD WINAPI WorkerThread(LPVOID);
    void Cleanup(int);int main()
    {
      WSA#     wsa#;
      SOCKET      sListen, sClient;
      SOCKADDR_IN local, client;
      DWORD       dwThreadId;
      int         iaddrSize = sizeof(SOCKADDR_IN);  // Initialize Windows Socket library
      WSAStartup(0x0202, &wsa#);  // Create listening socket
      sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  // Bind
      local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
     local.sin_family = AF_INET;
     local.sin_port = htons(PORT);
      bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));  // Listen
      listen(sListen, 3);  // Create worker thread
      CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);  while (TRUE)
      {
        // Accept a connection
        sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
        printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));    g_CliSocketArr[g_iTotalConn] = sClient;
        
        // Allocate a PER_IO_OPERATION_# structure
        g_pPerIO#Arr[g_iTotalConn] = (LPPER_IO_OPERATION_#)HeapAlloc(
          GetProcessHeap(),
          HEAP_ZERO_MEMORY,
          sizeof(PER_IO_OPERATION_#));
        g_pPerIO#Arr[g_iTotalConn]->Buffer.len = MSGSIZE;
        g_pPerIO#Arr[g_iTotalConn]->Buffer.buf = g_pPerIO#Arr[g_iTotalConn]->szMessage;
        g_CliEventArr[g_iTotalConn] = g_pPerIO#Arr[g_iTotalConn]->overlap.hEvent = WSACreateEvent();    // Launch an asynchronous operation
        WSARecv(
          g_CliSocketArr[g_iTotalConn],
          &g_pPerIO#Arr[g_iTotalConn]->Buffer,
          1,
          &g_pPerIO#Arr[g_iTotalConn]->NumberOfBytesRecvd,
          &g_pPerIO#Arr[g_iTotalConn]->Flags,
          &g_pPerIO#Arr[g_iTotalConn]->overlap,
          NULL);
        
        g_iTotalConn++;
      }
      
      closesocket(sListen);
      WSACleanup();
      return 0;
    }DWORD WINAPI WorkerThread(LPVOID lpParam)
    {
      int   ret, index;
      DWORD cbTransferred;  while (TRUE)
      {
        ret = WSAWaitForMultipleEvents(g_iTotalConn, g_CliEventArr, FALSE, 1000, FALSE);
        if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT)
        {
          continue;
        }    index = ret - WSA_WAIT_EVENT_0;
        WSAResetEvent(g_CliEventArr[index]);    WSAGetOverlappedResult(
          g_CliSocketArr[index],
          &g_pPerIO#Arr[index]->overlap,
          &cbTransferred,
          TRUE,
          &g_pPerIO#Arr[g_iTotalConn]->Flags);    if (cbTransferred == 0)
        {
          // The connection was closed by client
          Cleanup(index);
        }
        else
        {
          // g_pPerIO#Arr[index]->szMessage contains the received #
          g_pPerIO#Arr[index]->szMessage[cbTransferred] = '\0';
          send(g_CliSocketArr[index], g_pPerIO#Arr[index]->szMessage,\
            cbTransferred, 0);      // Launch another asynchronous operation
          WSARecv(
            g_CliSocketArr[index],
            &g_pPerIO#Arr[index]->Buffer,
            1,
            &g_pPerIO#Arr[index]->NumberOfBytesRecvd,
            &g_pPerIO#Arr[index]->Flags,
            &g_pPerIO#Arr[index]->overlap,
            NULL);
        }
      }  return 0;
    }void Cleanup(int index)
    {
      closesocket(g_CliSocketArr[index]);
      WSACloseEvent(g_CliEventArr[index]);
      HeapFree(GetProcessHeap(), 0, g_pPerIO#Arr[index]);  if (index < g_iTotalConn - 1)
      {
        g_CliSocketArr[index] = g_CliSocketArr[g_iTotalConn - 1];
        g_CliEventArr[index] = g_CliEventArr[g_iTotalConn - 1];
        g_pPerIO#Arr[index] = g_pPerIO#Arr[g_iTotalConn - 1];
      }  g_pPerIO#Arr[--g_iTotalConn] = NULL;
    }
     这个模型与上述其他模型不同的是它使用Winsock2提供的异步I/O函数WSARecv。在调用WSARecv时,指定一个 WSAOVERLAPPED结构,这个调用不是阻塞的,也就是说,它会立刻返回。一旦有数据到达的时候,被指定的WSAOVERLAPPED结构中的 hEvent被Signaled。由于下面这个语句
    g_CliEventArr[g_iTotalConn] = g_pPerIO#Arr[g_iTotalConn]->overlap.hEvent;
    使 得与该套接字相关联的WSAEVENT对象也被Signaled,所以WSAWaitForMultipleEvents的调用操作成功返回。我们现在应 该做的就是用与调用WSARecv相同的WSAOVERLAPPED结构为参数调用WSAGetOverlappedResult,从而得到本次I/O传 送的字节数等相关信息。在取得接收的数据后,把数据原封不动的发送到客户端,然后重新激活一个WSARecv异步操作。2.用完成例程方式实现的重叠I/O模型
    #include <WINSOCK2.H>
    #include <stdio.h>#define PORT    5150
    #define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")typedef struct
    {
     WSAOVERLAPPED overlap;
     WSABUF        Buffer;
      char          szMessage[MSGSIZE];
     DWORD         NumberOfBytesRecvd;
     DWORD         Flags; 
     SOCKET        sClient;
    }PER_IO_OPERATION_#, *LPPER_IO_OPERATION_#;DWORD WINAPI WorkerThread(LPVOID);
    void CALLBACK CompletionROUTINE(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);SOCKET g_sNewClientConnection;
    BOOL   g_bNewConnectionArrived = FALSE;int main()
    {
      WSA#     wsa#;
      SOCKET      sListen;
      SOCKADDR_IN local, client;
      DWORD       dwThreadId;
      int         iaddrSize = sizeof(SOCKADDR_IN);  // Initialize Windows Socket library
      WSAStartup(0x0202, &wsa#);  // Create listening socket
      sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  // Bind
      local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
     local.sin_family = AF_INET;
     local.sin_port = htons(PORT);
      bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));  // Listen
      listen(sListen, 3);  // Create worker thread
      CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);  while (TRUE)
      {
        // Accept a connection
        g_sNewClientConnection = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
        g_bNewConnectionArrived = TRUE;
        printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
      }
    }DWORD WINAPI WorkerThread(LPVOID lpParam)
    {
     LPPER_IO_OPERATION_# lpPerIO# = NULL;  while (TRUE)
      {
        if (g_bNewConnectionArrived)
        {
          // Launch an asynchronous operation for new arrived connection
          lpPerIO# = (LPPER_IO_OPERATION_#)HeapAlloc(
            GetProcessHeap(),
            HEAP_ZERO_MEMORY,
            sizeof(PER_IO_OPERATION_#));
          lpPerIO#->Buffer.len = MSGSIZE;
          lpPerIO#->Buffer.buf = lpPerIO#->szMessage;
          lpPerIO#->sClient = g_sNewClientConnection;
          
          WSARecv(lpPerIO#->sClient,
            &lpPerIO#->Buffer,
            1,
            &lpPerIO#->NumberOfBytesRecvd,
            &lpPerIO#->Flags,
            &lpPerIO#->overlap,
            CompletionROUTINE);      
          
          g_bNewConnectionArrived = FALSE;
        }    SleepEx(1000, TRUE);
      }
      return 0;
    }void CALLBACK CompletionROUTINE(DWORD dwError,
                                    DWORD cbTransferred,
                                    LPWSAOVERLAPPED lpOverlapped,
                                    DWORD dwFlags)
    {
      LPPER_IO_OPERATION_# lpPerIO# = (LPPER_IO_OPERATION_#)lpOverlapped;
      
      if (dwError != 0 || cbTransferred == 0)
     {
        // Connection was closed by client
      closesocket(lpPerIO#->sClient);
      HeapFree(GetProcessHeap(), 0, lpPerIO#);
     }
      else
      {
        lpPerIO#->szMessage[cbTransferred] = '\0';
        send(lpPerIO#->sClient, lpPerIO#->szMessage, cbTransferred, 0);
        
        // Launch another asynchronous operation
        memset(&lpPerIO#->overlap, 0, sizeof(WSAOVERLAPPED));
        lpPerIO#->Buffer.len = MSGSIZE;
        lpPerIO#->Buffer.buf = lpPerIO#->szMessage;        WSARecv(lpPerIO#->sClient,
          &lpPerIO#->Buffer,
          1,
          &lpPerIO#->NumberOfBytesRecvd,
          &lpPerIO#->Flags,
          &lpPerIO#->overlap,
          CompletionROUTINE);
      }
    }用完成例程来实现重叠I/O比用事件通知简单得多。在这个模型中,主线程只用不停的接受连接即可;辅助线程判断有没有新的客户端连接被建立,如果 有,就为那个客户端套接字激活一个异步的WSARecv操作,然后调用SleepEx使线程处于一种可警告的等待状态,以使得I/O完成后 CompletionROUTINE可以被内核调用。如果辅助线程不调用SleepEx,则内核在完成一次I/O操作后,无法调用完成例程(因为完成例程 的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内)。
    完成例程内的实现代码比较简单,它取出接收到的数据,然后将数据原封不动 的发送给客户端,最后重新激活另一个WSARecv异步操作。注意,在这里用到了“尾随数据”。我们在调用WSARecv的时候,参数 lpOverlapped实际上指向一个比它大得多的结构PER_IO_OPERATION_#,这个结构除了WSAOVERLAPPED以外,还 被我们附加了缓冲区的结构信息,另外还包括客户端套接字等重要的信息。这样,在完成例程中通过参数lpOverlapped拿到的不仅仅是 WSAOVERLAPPED结构,还有后边尾随的包含客户端套接字和接收数据缓冲区等重要信息。这样的C语言技巧在我后面介绍完成端口的时候还会使用到。
      

  10.   

    1)如果处理接收到的TCP消息需要消耗比较长时间。而且对性能有较高要求。建议采用多线程异步发送和接收方式。专门定义一个线程发送接收消息,然后通过消息队列,发送给业务线程处理。发消息也是发送数据到消息队列,然后由发送接收线程处理。2)发送接收线程通过buffer管理机制,发送不完的消息都缓存起来。每次发送一点。