重叠IO技术在服务器端的应用
来源:互联网 发布:用网络机顶盒怎么看电视直播 编辑:程序博客网 时间:2024/06/01 08:22
在处理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操作,还需要加入对应的WSAOVERLAPPED与EVENT,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;
}
- 重叠IO技术在服务器端的应用
- json在服务器端的应用
- IO重叠的理解
- IO重叠的理解
- 重叠模型IO的优点
- 在MFC中应用CTreeCtrl控件的技巧(对话框重叠)
- 重叠io
- 重叠IO
- 重叠IO
- 重叠IO
- IO重叠
- 重叠IO
- 重叠IO
- 重叠IO
- 使用重叠IO的命名管道服务器
- 基于事件的重叠IO模型
- 重叠IO的四种机制
- Chapter10-IO 重叠IO
- VISUALSTUDIO2003,2005 button_click button_click_1
- response.sendRedirect java.lang.IllegalStateException
- java中自定义标签的配置
- asp比较灵活的在线问答的示例
- 在将要离开公司的日子
- 重叠IO技术在服务器端的应用
- 辛苦的学习C++/VC
- 关于iis一些基本操作
- asp中导出Excel的方法
- 实验博客
- sql server中存儲過程的寫法格式規格
- JNI的使用
- 集群速度问题
- sql 存储过程分页