使用完成端口的单台服务器最多可连接2500个客户端
2007-03-04 17:00
417 查看
最近在做一个完成端口的项目,希望测试一下在普通Win2k Pro下最多可容纳的客户端。众所诸知,当客户connect到server,CreateIoCompletionPort后调用WSARecv等待客户端主动主报的数据。下面就是测试程序,单个客户端程序连接后,不停的调用wsarecv,观察非页面缓冲池的大小,发现到非页面缓冲池为800多K,而循环次数为2500的时候,程序崩溃。
wsarecv调用时会将指定的页面锁定到物理内存中,该页面不会被交换出去。。。
我的2500次wsarecv锁定了800多K.而且将DATA_BUFSIZE减少到8也没有任何改善情况。
于是程序就飞了,还好,系统没有发疯。win2k还是比较稳定的。
再加上创建socket等操作也需要占用非页面缓冲池,估计实际的单台服务器所连接的客户端比2500还要少。
在单个socket同一个overlapped上,如果同时存在wsarecv和wsasend,喝喝,也就是说在wsarecv一直存在,在wsasend末完成前wsarecv接收到了数据,如果在GetQueuedCompletionStatus时只好自己来决定是wsarecv还是wsasend.这种情况真的很难办。我无法决定是wsarecv返回还是wsasend返回.于是在单个socket同一个overlapped上,一次只能有一个wsarecv或wsasend,在调用wsasend之前,必须判断是否存在wsarecv,如有,调用cancelio取消,把占用的页面换出来。当然也可以只调用wsarecv,在收到数据后具体的事务处理中使用send,recv等就没这么多烦恼了。
如果存在多个wsarecv在单个socket上,其所占用的页面会一定直锁定,直到在该wsarecv收到数据或调用cancleio,或PostQueuedCompletionStatus或进程终止。。。为止。
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_BUFSIZE 8192
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
DWORD BytesSEND;
DWORD BytesRECV;
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD __stdcall ServerWorkerThread(LPVOID CompletionPortID){return 0;};
void main(void)
{
SOCKET Listen,Accept;
HANDLE CompletionPort;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD RecvBytes, Flags;
WSADATA wsaData;
if (( WSAStartup(0x0202, &wsaData)) != 0)
{
return;
}
if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
{
printf( "CreateIoCompletionPort failed with error: %d/n", GetLastError());
return;
}
if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("WSASocket() failed with error %d/n", WSAGetLastError());
return;
}
DWORD MemorySize = 256*1024*1024;
BOOL Result = FALSE;
do {
Result = SetProcessWorkingSetSize(GetCurrentProcess(), MemorySize, MemorySize*2);
if (!Result)
{
MemorySize -= 10*1024*1024;
}
} while ( !Result );
printf("MemorySize is %dM/n",MemorySize/1024/1024);
struct sockaddr_in stddr={0};
int iAddrLen = sizeof(struct sockaddr_in);
stddr.sin_family = AF_INET;
stddr.sin_addr.s_addr = htonl(INADDR_ANY);
stddr.sin_port = htons(PORT);
if (bind(Listen, (PSOCKADDR) &stddr, sizeof(stddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d/n", WSAGetLastError());
return;
}
if (listen(Listen, 1) == SOCKET_ERROR)
{
printf("listen() failed with error %d/n", WSAGetLastError());
return;
}
while(TRUE)
{
if ((Accept = WSAAccept(Listen,(sockaddr*) &stddr, &iAddrLen, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d/n", WSAGetLastError());
return;
}
printf("%s/n",inet_ntoa(stddr.sin_addr));
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d/n", GetLastError());
return;
}
printf("Socket number %d connected/n", Accept);
PerHandleData->Socket = Accept;
if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d/n", GetLastError());
return;
}
if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d/n", GetLastError());
return;
}
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->BytesSEND = 0;
PerIoData->BytesRECV = 0;
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
Flags = 0;
int iLoop = 0;
char chTemp[256] = {0};
while (++ iLoop)
{
if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d/n", WSAGetLastError());
return;
}
}
printf("WSARecv 循环次数为%d/n",iLoop);
sprintf(chTemp,"WSARecv 循环次数为%d/n",iLoop);
OutputDebugString(chTemp);
Sleep(10);
}
}
}
wsarecv调用时会将指定的页面锁定到物理内存中,该页面不会被交换出去。。。
我的2500次wsarecv锁定了800多K.而且将DATA_BUFSIZE减少到8也没有任何改善情况。
于是程序就飞了,还好,系统没有发疯。win2k还是比较稳定的。
再加上创建socket等操作也需要占用非页面缓冲池,估计实际的单台服务器所连接的客户端比2500还要少。
在单个socket同一个overlapped上,如果同时存在wsarecv和wsasend,喝喝,也就是说在wsarecv一直存在,在wsasend末完成前wsarecv接收到了数据,如果在GetQueuedCompletionStatus时只好自己来决定是wsarecv还是wsasend.这种情况真的很难办。我无法决定是wsarecv返回还是wsasend返回.于是在单个socket同一个overlapped上,一次只能有一个wsarecv或wsasend,在调用wsasend之前,必须判断是否存在wsarecv,如有,调用cancelio取消,把占用的页面换出来。当然也可以只调用wsarecv,在收到数据后具体的事务处理中使用send,recv等就没这么多烦恼了。
如果存在多个wsarecv在单个socket上,其所占用的页面会一定直锁定,直到在该wsarecv收到数据或调用cancleio,或PostQueuedCompletionStatus或进程终止。。。为止。
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 5150
#define DATA_BUFSIZE 8192
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
DWORD BytesSEND;
DWORD BytesRECV;
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD __stdcall ServerWorkerThread(LPVOID CompletionPortID){return 0;};
void main(void)
{
SOCKET Listen,Accept;
HANDLE CompletionPort;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD RecvBytes, Flags;
WSADATA wsaData;
if (( WSAStartup(0x0202, &wsaData)) != 0)
{
return;
}
if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
{
printf( "CreateIoCompletionPort failed with error: %d/n", GetLastError());
return;
}
if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("WSASocket() failed with error %d/n", WSAGetLastError());
return;
}
DWORD MemorySize = 256*1024*1024;
BOOL Result = FALSE;
do {
Result = SetProcessWorkingSetSize(GetCurrentProcess(), MemorySize, MemorySize*2);
if (!Result)
{
MemorySize -= 10*1024*1024;
}
} while ( !Result );
printf("MemorySize is %dM/n",MemorySize/1024/1024);
struct sockaddr_in stddr={0};
int iAddrLen = sizeof(struct sockaddr_in);
stddr.sin_family = AF_INET;
stddr.sin_addr.s_addr = htonl(INADDR_ANY);
stddr.sin_port = htons(PORT);
if (bind(Listen, (PSOCKADDR) &stddr, sizeof(stddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d/n", WSAGetLastError());
return;
}
if (listen(Listen, 1) == SOCKET_ERROR)
{
printf("listen() failed with error %d/n", WSAGetLastError());
return;
}
while(TRUE)
{
if ((Accept = WSAAccept(Listen,(sockaddr*) &stddr, &iAddrLen, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d/n", WSAGetLastError());
return;
}
printf("%s/n",inet_ntoa(stddr.sin_addr));
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d/n", GetLastError());
return;
}
printf("Socket number %d connected/n", Accept);
PerHandleData->Socket = Accept;
if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d/n", GetLastError());
return;
}
if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d/n", GetLastError());
return;
}
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->BytesSEND = 0;
PerIoData->BytesRECV = 0;
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
Flags = 0;
int iLoop = 0;
char chTemp[256] = {0};
while (++ iLoop)
{
if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d/n", WSAGetLastError());
return;
}
}
printf("WSARecv 循环次数为%d/n",iLoop);
sprintf(chTemp,"WSARecv 循环次数为%d/n",iLoop);
OutputDebugString(chTemp);
Sleep(10);
}
}
}
相关文章推荐
- 使用完成端口的单台服务器最多可连接2500个客户端
- 修改客户端连接的服务器IP地址(内部使用)
- Android网络客户端编程,HttpGet类和HttpPost类使用详解,连接php-mysql服务器
- 在Linux上(我的服务器是Ubuntu) 用redis-trib.rb搭建redis集群,并在客户端使用spring-data-redis连接(亲测)
- WebSocket 的使用,和客户端断电,服务器检测断开连接
- 未安装Oracle客户端的服务器上,使用ASP.NET远程连接Oracle
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
- Java和Android Http连接程序:使用java.net.URL 下载服务器图片到客户端
- Windows Server 2003 使用vSphere5.5连接ESXI5.5 “客户端无法向服务器发送完整的请求” 的解决办法
- 如何使用MSTSC命令远程连接服务器或客户端
- 设置Outlook客户端连接Exchange 2010服务器在低速或高速网络均使用https
- PLSQL使用oracle精简客户端连接Oracle数据库服务器
- 使用同一账号在多个xmpp客户端连接openfire服务器的解决方案
- Oracle 不安装Oracle客户端,使用PLSQL连接Oracle服务器
- oracle:不安装oracle客户端,连接其他服务器,使用pl/sql Developer
- 连接远程oracle和SVN服务器客户端的使用
- Android 使用Socket实现服务器与手机客户端的长连接四:使用回调函数与Service调用
- VS2008 使用 occi 连接 Oracle 服务器- 不用安装客户端
- oracle精简版客户端安装及使用PLSQL Developer连接oracle服务器
- Android使用Gson解析JSON连接服务器实现客户端登陆功能