C语言辅导:面向短连接的网络服务器计算机二级考试
文章作者 100test 发表时间 2009:04:30 07:45:31
来源 100Test.Com百考试题网
2009年下半年全国计算机等级考试你准备好了没?考计算机等级考试的朋友,2009年下半年全国计算机等级考试时间是2009年9月19日至23日。更多优质资料尽在百考试题论坛 百考试题在线题库
前几天同事用sf上的一个网络类库写了个服务器。一测试发现性能很差。最多每秒才能处理500次请求,并且是在网络很好的情况下,隔两个交换机后客户端就只能收到200次/秒的正确响应了。同事忙着做其它事,改进服务器的任务就交给我了。
项目中客户端的请求仅是有20bytes的数据,并且只有一小部分需要服务器回复500bytes左右的数据。综合考虑各种网络模型后我决得IOCP模型更适合当前的应用。
IOCP模型的使用方法很多资料都有。《windows网络编程》(第二版)讲得很好。随书光盘中有使用IOCP模型的简单但很好的例子。我的服务器程序写完时还没看到这个示例,正被其它示例代码里的锁弄的晕头转向。当看到这个示例后发现那些锁都是多余的。剩下的代码基本上差不多。
有点不同的是PER_IO_DATA的处理上。所有的例子在往完成端口投递请求时都先分配了一个PER_IO_DATA,请求处理后马上释放。考虑到我的应用中所有请求全部是短连接,并且数据量很小,我觉得分配和释放是浪费的。每个PER_IO_DATA对应一个socket,当socket关闭后这个PER_IO_DATA不必释放,而是用来准备接受下一个连接socket。
下面是写的工作线程代码:
DWORD WINAPI ServerWorkerThread(LPVOID lpparam)
{
CIocpServer * pServer = (CIocpServer*)lpparam.
DWORD BytesTransferred.
SOCKET socket.
LPPER_IO_OPERATION_DATA PerIoData.
BOOL close_socket = FALSE.
int ret.
while(pServer->.bRun)
{
ret = GetQueuedCompletionStatus(pServer->.CompletionPort, &.BytesTransferred,
(LPDWORD)&.socket, (LPOVERLAPPED *) &.PerIoData, INFINITE).
if (ret == ERROR_SUCCESS)
{
DWORD last_error = GetLastError().
if(last_error == ERROR_SUCCESS)
return 0. //完成端口被关闭,退出
if(ERROR_NETNAME_DELETED == last_error
|| ERROR_OPERATION_ABORTED == last_error)
close_socket = TRUE. //socket被关闭 或者 操作被取消
else
continue.
}
if (BytesTransferred == 0)
{
if(socket == 0 &.&. PerIoData == 0)
break.
closesocket(PerIoData->.socket).
pServer->.Accept(PerIoData).
continue.
}
if(PerIoData->.eType == IO_EVENT_ACCEPT)
{
setsockopt(PerIoData->.socket,SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&.(pServer->.m_server), sizeof(pServer->.m_server) ) .
if(CreateIoCompletionPort((HANDLE) PerIoData->.socket, pServer->.CompletionPort, (DWORD) PerIoData->.socket,0) == NULL)
printf("CreateIoCompletionPort error:%d ", GetLastError()).
ret = pServer->.Recv(PerIoData,BytesTransferred).
}
else if(PerIoData->.eType == IO_EVENT_WSARECV)
{
ret = pServer->.Recv(PerIoData,BytesTransferred).
}
else if(PerIoData->.eType == IO_EVENT_WSASEND)
{
ret = pServer->.Send(PerIoData,BytesTransferred).
}
if(ret == FALSE)
{
closesocket(PerIoData->.socket).
pServer->.Accept(PerIoData).
}
}
return 0.
}
可以看到,每个AccepteEx接入的客户端连接对应着一个PER_IO_DATA,并伴随着该连接的整个生命周期。百考试题提示当连接结束后,PER_IO_DATA马上又通过投递AccepteEx准备下一个连接。这样可以避免平凡的分配和释放内存。