一个win32下的完成端口模式网络编程例子
来源:互联网 发布:淘宝食品如何备案 编辑:程序博客网 时间:2024/05/28 17:07
一个win32下的完成端口模式网络编程例子
一个一般的网络服务程序的工作流程是:启动监听服务→等待客户连接→接受客户连接→创建新的线程处理客户的请求→客户中断连接退出线程。
然而创建和撤销线程需要开销,成为影响性能的瓶颈。假若我们能够预先创建好线程,然后用这些线程为不同的客户处理数据请求,而不必为每个客户创建新的线程,那么我们的服务器性能将得到大幅度的提高,尤其要同时处理大量的套接字时。想想,系统不能无限的创建线程,当需要同时处理大量的套接字时,利用有限的线程为大量的客户连接提供服务就很有必要了。
win32API为我们提供一种完成端口模式的I/O模型用于网络编程。下面是一个例子:
头文件
#pragma once
#include "RSA.h"
#include "AES.h"
#include "AccountManageClass.h"
#define SOCKET_OPT_PROC 1
#define SOCKET_OPT_RECV 2
#define SOCKET_OPT_QUIT 3
//客户端的单句柄数据类型
typedef struct _SPER_HANDLE_DATA
{
SOCKET sockfd; //连接套接字
…… //其他类型数据
…… //其他类型数据
}SPER_HANDLE_DATA, *LPSPER_HANDLE_DATA;
//单IO操作数据
typedef struct _SPER_IO_DATA{
OVERLAPPED overlapped;
WSABUF buffer; //一个数据缓冲区,用于WSASend/WSARecv中的第二个参数
char dataBuffer[BUFF_LEN];//实际的数据缓冲区
int dataLength; //实际的数据缓冲区长度
int operatorType; //操作类型
}SPER_IO_DATA, *LPSPER_IO_DATA;
class ServerClass
{
public:
staticconst int LISTEN_NUMER = 32; //监听队列数量
staticconst int CHECK_CONNECT_TIME_OUT = 10 * 60 * 1000; //监测连接超时时间间隔
private:
staticDWORD WINAPI ServerAcceptThread(LPVOID lpParam); //接受连接线程
staticDWORD WINAPI ServerWorkerThread(LPVOID lpParam); //工作者线程
staticDWORD WINAPI CheckConnectThread(LPVOID lpParam); //定时检测连接状态函数
public:
ServerClass();
~ServerClass();
boolServerStart(short port); //启动服务
voidAcceptThread(HANDLE CompletionPort); //接受连接处理
voidWorkerThread(HANDLE CompletionPort); //具体工作处理
voidConnectThread(); //定时监测处理
private:
voidServerStop(); //关闭服务
voidInit(); //初始化相关变量
//释放资源
voidFreeSource(LPSPER_HANDLE_DATA perHandleData, LPSPER_IO_DATA perIoData);
private:
boolm_inited; //是否已初始化标志
SOCKETm_listen; //TheListen socket
CRITICAL_SECTIONm_section; //Crucial sourceprotected
list<SOCKET>m_custom_sockets; //The connectedsockets
HANDLEm_accept_thread; //Acceptconnect process thread handle
HANDLEm_check_thread; //Checkconnect timeout thread handle
};
//用于传给线程的参数结构
typedef struct _THREAD_PARAM
{
HANDLECompletionPort;
ServerClass *object;
}THREAD_PARAM, *LPTHREAD_PARAM;
源文件
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <mswsock.h>
#include <string>
#include <iterator>
#include <list>
using namespace std;
#include "ServerClass.h"
using namespace std;
#pragma comment(lib,"ws2_32.lib")
ServerClass::ServerClass()
{
m_inited= false;
Init();
}
ServerClass::~ServerClass()
{
ServerStop();
}
void ServerClass::Init()
{
m_accept_thread= NULL;
m_check_thread= NULL;
InitializeCriticalSection(&m_section);
m_inited= true;
}
bool ServerClass::ServerStart(short port)
{
if(!m_inited)
Init();
//创建完成端口句柄
HANDLECompletionPort;
CompletionPort= CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
if(NULL== CompletionPort)
{
perror("ServerStart(): WSAStartup failed: %d", GetLastError());
returnfalse;
}
//根据系统的CPU核数创建处理线程
SYSTEM_INFOinfo;
GetSystemInfo(&info);
for(DWORDi=0; i < info.dwNumberOfProcessors; i++)
{
LPTHREAD_PARAMlpParam = new THREAD_PARAM;
lpParam->CompletionPort= CompletionPort;
lpParam->object= this;
HANDLEThreadHandle = CreateThread(NULL, 0, ServerClass::ServerWorkerThread, lpParam,0, NULL);
if(NULL== ThreadHandle)
{
deletelpParam;
CloseHandle(ThreadHandle);
}
}
//为防止用户建立连接不发送数据,创建检测连接超时线程,将此类连接关断
{
LPTHREAD_PARAMlpParam = new THREAD_PARAM;
lpParam->CompletionPort= CompletionPort;
lpParam->object= this;
m_check_thread= CreateThread( NULL, 0, ServerClass::CheckConnectThread, lpParam, 0, NULL);
if(NULL== m_check_thread)
{
deletelpParam;
perror("CreateCheckConnectThread failed");
returnfalse;
}
}
//启动socket库
WSADATAdata;
if(WSAStartup(MAKEWORD(2,2),&data)!= 0)
{
perror("ServerStart(): WSAStartup failed: %d", WSAGetLastError());
returnfalse;
}
m_listen= WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
if(INVALID_SOCKET== m_listen)
{
perror("ServerStart(): Listen = WSASocket failed: %d",WSAGetLastError());
returnfalse;
}
//Setreuse the address, then can use the same socket address immediately whenrestart the server
intnOpt = 1;
setsockopt(m_listen,SOL_SOCKET, SO_REUSEADDR, (char*)&nOpt, sizeof(nOpt));
SOCKADDR_INaddr;
addr.sin_family= AF_INET;
addr.sin_addr.s_addr= htonl(INADDR_ANY);
addr.sin_port= htons(port);
if(SOCKET_ERROR== bind(m_listen,(PSOCKADDR)&addr,sizeof(addr)))
{
perror("ServerStart(): bind failed: %d", WSAGetLastError());
returnfalse;
}
if(listen(m_listen,ServerClass::LISTEN_NUMER) == SOCKET_ERROR)
{
perror("ServerStart(): listen failed: %d", WSAGetLastError());
returnfalse;
}
//创建监听接受客户连接线程
{
LPTHREAD_PARAMlpParam = new THREAD_PARAM;
lpParam->CompletionPort= CompletionPort;
lpParam->object= this;
m_accept_thread= CreateThread( NULL, 0, ServerClass::ServerAcceptThread, lpParam, 0, NULL);
if(NULL== m_accept_thread)
{
deletelpParam;
perror("CreateUpdateRegisterThread failed");
returnfalse;
}
}
returntrue;
}
void ServerClass::ServerStop()
{
if(NULL!= m_check_thread)
{
TerminateThread(m_check_thread,0);
m_check_thread= NULL;
}
//closeall connected socket
EnterCriticalSection(&m_section);
for(list<SOCKET>::iteratoriter = m_custom_sockets.begin(); iter != m_custom_sockets.end(); iter++)
{
if(*iter!= INVALID_SOCKET)
closesocket(*iter);
}
LeaveCriticalSection(&m_section);
closesocket(m_listen);
WaitForSingleObject(m_accept_thread,1000);
WSACleanup();
DeleteCriticalSection(&m_section);
m_inited= false;
}
voidServerClass::FreeSource(LPSPER_HANDLE_DATA perHandleData, LPSPER_IO_DATAperIoData)
{
//现将该socket从检测队列中删除
EnterCriticalSection(&m_section);
if(NULL!= perHandleData && closesocket(perHandleData->sockfd) !=SOCKET_ERROR)
{
for(list<SOCKET>::iteratoriter = m_custom_sockets.begin(); iter != m_custom_sockets.end();)
{
if(*iter== perHandleData->sockfd)
{
m_custom_sockets.erase(iter++);
break;
}
++iter;
}
}
LeaveCriticalSection(&m_section);
//再释放所申请的内存
if(NULL!= perHandleData)
{
GlobalFree(perHandleData);
perHandleData= NULL;
}
if(NULL!= perIoData)
{
GlobalFree(perIoData);
perIoData= NULL;
}
#ifdef _DEBUG
perror("closesocketand free perHandleData perIoData!");
#endif
}
void ServerClass::AcceptThread(HANDLECompletionPort)
{
SOCKETAccept;
LPSPER_HANDLE_DATAPerHandleData = NULL;
LPSPER_IO_DATAperIoData = NULL;
DWORDSendBytes;
while(true)
{
#ifdef _DEBUG
perror("Waitfor new connect...");
#endif
Accept= WSAAccept(m_listen, NULL, NULL, NULL, 0);
if(INVALID_SOCKET== Accept) //the listen socket hasbeen closed
break;
#ifdef _DEBUG
perror("Accept%d connect...", Accept);
#endif
PerHandleData= (LPSPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(SPER_HANDLE_DATA));
if(NULL== PerHandleData)
{
perror("GlobalAllocPerHandleData failed: %d", GetLastError());
closesocket(Accept);
continue;
}
perIoData= (LPSPER_IO_DATA)GlobalAlloc(GPTR, sizeof(SPER_IO_DATA));
if(NULL== perIoData)
{
perror("GlobalAllocperIoData failed: %d", GetLastError());
GlobalFree(PerHandleData);
closesocket(Accept);
continue;
}
PerHandleData->sockfd= Accept;
ZeroMemory(&(perIoData->overlapped),sizeof(OVERLAPPED));
if(NULL== CreateIoCompletionPort((HANDLE)Accept, CompletionPort, (DWORD)PerHandleData,0))
{
perror("CreateIoCompletionPortfailed: %d", GetLastError());
GlobalFree(PerHandleData);
GlobalFree(perIoData);
closesocket(Accept);
continue;
}
LINGERlinger = {1, 1};
setsockopt(Accept,SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger));
//这里开始接收或发送第一个数据包(这里代码是发送,如果是接收请用WSARecv,并根据居体情况设置perIoData->operatorType
perIoData->buffer.buf= (char*)&(perIoData->dataBuffer);
perIoData->buffer.len= perIoData->dataLength = sizeof(perIoData->dataBuffer);
perIoData->operatorType= SOCKET_OPT_RECV;
if(WSASend(Accept,&(perIoData->buffer), 1, &SendBytes, 0, &(perIoData->overlapped),NULL) == SOCKET_ERROR)
{
if(WSAGetLastError()!= WSA_IO_PENDING)
{
perror("Sendpublic key failed");
GlobalFree(PerHandleData);
GlobalFree(perIoData);
closesocket(Accept);
continue;
}
}
EnterCriticalSection(&m_section);
m_custom_sockets.push_back(Accept);
LeaveCriticalSection(&m_section);
}
}
void ServerClass::WorkerThread(HANDLECompletionPort)
{
DWORDbytes;
LPSPER_HANDLE_DATAperHandleData = NULL;
LPSPER_IO_DATAperIoData = NULL;
DWORDFlags;
intret;
DWORDRecvBytes, SendBytes;
while(true)
{
bytes= -1;
ret= GetQueuedCompletionStatus(CompletionPort, &bytes,(LPDWORD)&perHandleData, (LPOVERLAPPED*)&perIoData, INFINITE);
//如果连接的客户已关闭套接字或出现错误,则关闭套接字,并释放资源
if(bytes== 0 || perIoData->operatorType == SOCKET_OPT_QUIT)
{
FreeSource(perHandleData,perIoData);
continue;
}
if(perIoData->operatorType== SOCKET_OPT_PROC)
{/*上一次操作WSASend或WSARecv设置perIoData->operatorType为SOCKET_OPT_PROCm,则当WSASend或WSARecv操作完成奖调用此处的代码*/
/*在这里可以对收发结果进行处理
若为WSARecv则接收到的数据存放在perIoData的dataBuffer中,接收到的数据长度为perIoData 的dataLength。
如果这里会对数据进行处理并要把数据发送给客户,则调用下面的的代码*/
if(需要发送数据)
{
memset(&(perIoData->dataBuffer),0, sizeof(MESSAGE_PACKAGE));
memcpy(&(perIoData->dataBuffer),&发送的数据, sizeof(MESSAGE_PACKAGE));
perIoData->buffer.buf= (char*)&(perIoData->dataBuffer);
perIoData->buffer.len= perIoData->dataLength;
perIoData->operatorType= SOCKET_OPT_RECV;
if(WSASend(perHandleData->sockfd,&(perIoData->buffer), 1, &SendBytes, 0,&(perIoData->overlapped), NULL) == SOCKET_ERROR)
{
if(WSAGetLastError()!= WSA_IO_PENDING)
{
FreeSource(perHandleData,perIoData);
}
}
continue;
}
}
//如果是其他操作,则继续接收客户发送的数据
ZeroMemory(&(perIoData->overlapped),sizeof(OVERLAPPED));
memset(&perIoData->dataBuffer,0, sizeof(perIoData->dataBuffer));
Flags= 0;
perIoData->buffer.buf= (char*)&(perIoData->dataBuffer);
perIoData->buffer.len= perIoData->dataLength = sizeof(perIoData->dataBuffer);
perIoData->operatorType= SOCKET_OPT_PROC;
if(WSARecv(perHandleData->sockfd,&(perIoData->buffer),1, &RecvBytes,&Flags, &(perIoData->overlapped),NULL) ==SOCKET_ERROR)
{
if(WSAGetLastError()!= WSA_IO_PENDING)
{
FreeSource(perHandleData,perIoData);
}
}
}
}
void ServerClass::ConnectThread()
{
intret;
intnSeconds;
intnLen = sizeof(nSeconds);
return;
while(true)
{
Sleep(ServerClass::CHECK_CONNECT_TIME_OUT);
//Closeall connect timeout socket
EnterCriticalSection(&m_section);
for(list<SOCKET>::iteratoriter = m_custom_sockets.begin(); iter != m_custom_sockets.end();)
{
ret= getsockopt(*iter, SOL_SOCKET, SO_CONNECT_TIME, (char *)&nSeconds,&nLen);
if(nSeconds!= -1 && nSeconds > 60 || ret == SOCKET_ERROR)
{
closesocket(*iter);
m_custom_sockets.erase(iter++);
#ifdef _DEBUG
perror("Connecttimeout...");
#endif
continue;
}
++iter;
}
LeaveCriticalSection(&m_section);
}
}
DWORD WINAPIServerClass::ServerAcceptThread(LPVOID lpParam)
{
HANDLECompletionPort = ((LPTHREAD_PARAM)lpParam)->CompletionPort;
ServerClass*object = ((LPTHREAD_PARAM)lpParam)->object;
object->AcceptThread(CompletionPort);
deletelpParam;
return0;
}
DWORD WINAPIServerClass::ServerWorkerThread(LPVOID lpParam)
{
HANDLECompletionPort = ((LPTHREAD_PARAM)lpParam)->CompletionPort;
ServerClass*object = ((LPTHREAD_PARAM)lpParam)->object;
object->WorkerThread(CompletionPort);
deletelpParam;
return0;
}
DWORD WINAPIServerClass::CheckConnectThread(LPVOID lpParam)
{
#ifdef_DEBUG
perror("CheckConnectThread...");
#endif
ServerClass*object = ((LPTHREAD_PARAM)lpParam)->object;
object->ConnectThread();
deletelpParam;
return0;
}
调用ServerStop()启动服务后要保持主线程运行,否则主线程结束,程序将退出。
可以这样使用
int main()
{
HANDLE hHandle =CreateEvent(NULL, TRUE, FALSE, NULL);
ServerClass server;
if(server.ServerStart())
{
printf("%sfile %d line: Server Ready...", __FILE__, __LINE__);
}
WaitForSingleObject(hHandle,INFINITE);
}
- 一个win32下的完成端口模式网络编程例子
- 完成端口的一个例子
- 完成端口的一个例子
- 完成端口的一个例子
- 完成端口的一个例子
- 完成端口的一个例子
- 完成端口模式下的高性能网络服务器
- 完成端口模式下的高性能网络服务器
- 完成端口模式下的高性能网络服务器
- 完成端口(iocp)的一个例子
- 网络编程--走出完成端口的误区
- socket编程之完成端口(附一个简单的IOCP例子)
- socket编程之完成端口(附一个简单的IOCP例子)
- socket编程之完成端口(附一个简单的IOCP例子)
- 网络编程(完成端口IOCP)
- 网络编程中的完成端口
- Windows网络编程 — UDP完成端口的实现
- 网络编程——走出"完成端口"的误区
- java 处理zip 压缩与解压的问题
- Jsp网络拓扑图--用java实现网络拓扑图
- C/C++ error: storage size of ‘tv’ isn’t known错误解决方法
- OSGi (一) 模块化和Java中模块化的弊病
- 关于rt3070wifi移植遇见问题求解
- 一个win32下的完成端口模式网络编程例子
- ognl表达式的简单用法
- 使用HttpURLConnection访问web页面
- cuda上使用printf函数
- 最为详尽的WPF类继承关系
- 9周项目2
- Jersey学习笔记
- 码流 / 码率 / 比特率 / 帧速率 / 分辨率 / 高清的区别
- 局域网IP规划