重叠IO技术在服务器端的应用

原创 2007年09月11日 16:18:00

在处理TCP并发线程上,一般我们采用一个线程处理接受客户端的连接,然后开出一个线程处理与这个客户端的交互过程.

但是,跟据机器的性能与操作系统的限制,一般在几百个并发线程,也就是同时处理几百个客户端连接.

 

为了处理更多的客户端连接,不得不考虑换一种网络模型,目前比较流行的有select,完成端口,重叠IO,本文主要介绍重叠IO的实现

 

//重叠IO投递WSARecv的例子

#include "stdafx.h"

#include <winsock2.h>

#include <vector>

#include "../../SJLib/rutil/Log.hpp"

#pragma comment(lib,"ws2_32.lib")

#pragma comment(lib,"../../SJLib/rutil/Debug/rutil.lib")

using namespace std;

using namespace SJLib;

 

#define MAX_SOCKET (WSA_MAXIMUM_WAIT_EVENTS)       //最大为64个事件句柄

#define DATA_BUFSIZE 4096                               //缓冲区大小

WSAEVENT eventArrays[MAX_SOCKET];                       //建立64个事件句柄,主要目的为了用WSAWaitForMultiEvent同时等待64个事件

 

typedef struct tagSocketInfo                            //如果打算支持send操作,还需要加入对应的WSAOVERLAPPEDEVENT,WSABUF,以满足不同时候的调用

{

     SOCKET                 _socket;                    //套接字

     SOCKADDR_IN            _address;                   //客户端主机地址与端口

     WSAOVERLAPPED      _overlapped;                //重叠IO最重要的结构WSAOVERLAPPED,msdn

     WSABUF                 _data;                           //WSARecv时需要用到的缓冲结构,len为长度,buf为程序提供的缓冲区指针

     char               _buffer[DATA_BUFSIZE];      //接收时用到的缓冲区,需要把它的地址赋给_data.buf

}SocketInfo,*LPSocketInfo;

 

SocketInfo SocketArrays[MAX_SOCKET];               //定义64个客户端连接信息

SOCKET hListen;                                              //服务端监听套接字

bool listenDone = false;                                //服务端是否打算退出

HANDLE hClientThread;                                   //接收数据线程句柄

 

 

BOOL WINAPI ShutdownHandler(DWORD dwCtrlType)      //用户关闭程序时,所做的释放操作

{

     listenDone = true;

     shutdown(hListen,0);

     closesocket(hListen);

 

     WaitForSingleObject(hClientThread,INFINITE);

     CloseHandle(hClientThread);

 

     for(int i=0;i<MAX_SOCKET;i++)

     {

         WSACloseEvent(eventArrays[i]);

     }

 

     InfoLog(<<"server exit"<<endl);

     exit(0);

     return TRUE;

}

 

DWORD WINAPI WorkerThread(LPVOID lpContext)

{

     int count = 0;

     while(!listenDone)                                               //判断服务端口是否退出

     {

         //精华就都在这里了,WSAWaitForMultipleEvents同时等待64个客户端数据到达事件,

         //第三个参数很重要,TRUE,也就是说当所有客户端都发数据过来,此函数再返回,FALSE则反

         //如果没有任何一个客户端发送数据过来,它将阻塞

         int index = WSAWaitForMultipleEvents(MAX_SOCKET,eventArrays,FALSE,WSA_INFINITE,FALSE);

         if(index!=WAIT_TIMEOUT)          //判断是否超时,当然这里不可能超时,因为我使用了无限等待

         {

              index -= WAIT_OBJECT_0;     //取入被触发的事件索引

              WSAResetEvent(eventArrays[index]);             //为了下一次使用,重置它

              LPSocketInfo client = &SocketArrays[index];

 

              InfoLog(<<count++<<" client recv buffer:"<<client->_buffer<<endl);         //这里就是本轮接受到的数据

              DWORD dwBytesTransferred;

              DWORD Flags = 0;

 

              //查询这次重叠IO的结果,主要是判断客户端是否断开

              WSAGetOverlappedResult(client->_socket,&client->_overlapped,&dwBytesTransferred, FALSE, &Flags);

              if(dwBytesTransferred == 0)          //断开

              {

                   closesocket(client->_socket);

                   continue;

              }

              else                                 //将继续接收下一轮数据,可以考虑在这里加入用WSASend回复客户端的代码,它也可以使用重叠IO技术,如果没有使用,就可能造成一个客户端的延迟拖慢了所有的

              {

                   DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;

                   memset(&client->_overlapped,0,sizeof(WSAOVERLAPPED));        //必须重新初始化相当的结构数据,前面我没有做,结果只接收到几十次,就停下来了

                   client->_overlapped.hEvent = eventArrays[index];

                   memset(client->_buffer,0,DATA_BUFSIZE);

                   client->_data.len = DATA_BUFSIZE;

                   client->_data.buf = client->_buffer;

                   if(WSARecv(client->_socket ,&client->_data,1,&dwRecvBytes,&Flags,&client->_overlapped, NULL)==SOCKET_ERROR)//继续接收

                   {

                       if(WSAGetLastError() != WSA_IO_PENDING)

                       {

                            shutdown(client->_socket,0);

                            closesocket(client->_socket);

                            continue;

                       }

                   }

              }

 

             

         }

     }

    

     return 0;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

     Log::initializeLog(Log::Cout|Log::VSDebugWindow,Log::Debug,"SimpleServer");

 

     SetConsoleCtrlHandler(ShutdownHandler,TRUE);

     WSADATA data = {0};

     WSAStartup(0x0202,&data);

 

     hListen = socket(AF_INET,SOCK_STREAM,0);                     //建立服务器端口

     struct sockaddr_in my_addr;

     memset(&my_addr,0,sizeof(sockaddr_in));

     my_addr.sin_family = AF_INET;

     my_addr.sin_port = htons(8000);

 

     my_addr.sin_addr.s_addr=inet_addr("192.168.1.230");

 

     int reuse = 0;

     if (setsockopt(hListen, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,sizeof(reuse)) != 0)

     {

         closesocket(hListen);

         ErrLog(<<"SocketBaseException: setsockopt server address fault"<<endl);

         return -1;

     }

 

     if(bind(hListen, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) != 0)

     {

         ErrLog(<<"SocketBaseException: bind server address fault"<<endl);

         return -1;

     }

 

     ULONG NonBlock = 1;

 

     if(listen(hListen,5) == SOCKET_ERROR)                        //开始监听

     {

         ErrLog(<<"listen socket fault"<<endl);

         return -1;

     }

    

     for(int i=0;i<MAX_SOCKET;i++)                                    //先把64个事件创建起来

     {

         eventArrays[i] = WSACreateEvent();

     }

 

     HANDLE hClientThread = CreateThread(NULL,0,WorkerThread,NULL,0,0);         //启动数据接收处理线程

 

     int count = 0;

     while(!listenDone)

     {

         LPSocketInfo client = &SocketArrays[count];                      //取出一个客户端连接句柄,等待客户端连入

        

          int addressLen = sizeof(client->_address);

         client->_socket = accept(hListen,(sockaddr*)&client->_address,&addressLen);         //在这里等待客户端连接

         const char *host = inet_ntoa(client->_address.sin_addr);                      

         InfoLog(<<"client host "<<host<<":"<<client->_address.sin_port<<endl);              //有客户端连入,打印对方主机地址与端口

        

         client->_overlapped.hEvent = eventArrays[count];                           //取入一个事先建立好的事件句柄分配给当前客户端

         client->_data.len = DATA_BUFSIZE;                                              //指定缓冲区大小

         client->_data.buf = client->_buffer;                                       //指定缓冲区地址

         DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;

         count++;                                                                       //计算器加一,注意,没有处理当客户端大于64以上的代码

 

         int nRel;

         //第一轮的接收,注意这里并没有阻塞,数据到达时,将会把它所使用的事件句柄设为有信号,也就是在WorkerThread里处理

         if((nRel = WSARecv(client->_socket ,&client->_data,1,&dwRecvBytes,&Flags,&client->_overlapped, NULL))

              == SOCKET_ERROR)

         {

              if(nRel==0)//客户端断开连接

              {

                   shutdown(client->_socket,0);

                   closesocket(client->_socket);

                   continue;

              }

              if(WSAGetLastError() != WSA_IO_PENDING)//如果结果不是WSA_IO_PENDING,就有可能是客户端一个包也没有发就断开了

              {

                   shutdown(client->_socket,0);

                   closesocket(client->_socket);

                   continue;

              }

         }

     }

    

     return 0;

}

 

 

 

聊天室项目服务器端的并发问题——使用线程池技术(1)

一、原因 之前在聊天室项目中,服务器端模型的选择了单客户端单线程,统一accept()的模型,大致就是一个在一个线程中循环accept接受客户端连接,接受到一个连接就创建一个专用线程。在客户...

最近一段时间要做的事情就是它了——Java服务器端的相关技术

原文链接 http://www.jianshu.com/p/814d05ddb54a 应届毕业生如何成为一名服务器端开发工程师(一) 字数1298 阅读1860 评论1...

Android应用开发-------------WebView(一)之WebView与服务器端的Js交互

最近公司再添加功能的时候,有一部分功能是用的html,在一个浏览器或webview中展示出html即可。当然在这里我们当然用webview控件喽   WebApp的好处: 在应用里嵌套web的好处有这...
  • ls703
  • ls703
  • 2015年05月05日 18:21
  • 2326

抛弃LCDS和FMS,在tomcat下开发Red5应用(第四篇)-客户端和服务器端的方法互相调用

客户端和服务器端的方法相互调用比较重要,在线列表基本上全是用这种方式实现的,当然也有使用RemoteSharedObject来实现的,但本人不太喜欢用RemoteSharedObject,只是用Rem...

服务器端的取值在前端应用之“=”或“:”

把服务端值,拿到前端可以直接来用,想必都知道是怎么回事;但是当我们赋值的时候,往往会在“=”或“:”下徘徊不定;当我们不知道两者的区别的时候,很有可能给我们带来很大的困扰。 今天遇到这样的这样的一种情...

重叠IO的管道服务器程序

  • 2008年01月30日 18:03
  • 11KB
  • 下载

服务器编程-重叠IO原理

  • 2008年09月11日 09:07
  • 97KB
  • 下载

一种基于 HTTP 长连接的“服务器推”技术在web端的应用

“服务器推”技术的应用 很多应用譬如监控、即时通信、即时报价系统都需要将后台发生的变化实时传送到客户端而无须客户端不停地刷新、发送请求。本文首先介绍、比较了常用的“服务器推”方案,着重介绍了 ...

Web开发——服务器端应用技术简单比较

在开发动态网站时,离不开服务器端技术,服务器端技术主要有CGI、ASP、PHP、ASP.NET和JSP。...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:重叠IO技术在服务器端的应用
举报原因:
原因补充:

(最多只允许输入30个字)