用C写了个简单的IOCP服务端,该服务不做什么事情,仅仅做的事情就是接受客户端的连接。测试服务端最多支持的连接数。服务端代码,如下:
#include <stdio.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define SVR_DATA_BUF_SIZE 8000
#define OP_READ 1
#define OP_WRITE 2
#define OP_ACCEPT 3
typedef struct
{
OVERLAPPED overlapped;
WSABUF databuf;
CHAR buffer[SVR_DATA_BUF_SIZE];
int nOp : 1;
DWORD send_bytes;
DWORD recv_bytes;
} per_io_operation_data, *lp_per_io_operation_data;
typedef struct
{
SOCKET sock;
} per_data_key, *lp_per_data_key;
volatile int iConnected = 0;
DWORD WINAPI svr_worker_thread(LPVOID lpParam);
int __cdecl main(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
SOCKET sckListener, sckAccepted;
HANDLE hCompletionPort;
SYSTEM_INFO system_info;
struct sockaddr_in sagni, saclient;
int iclientSize = sizeof(saclient);
char hostname[NI_MAXHOST];
char servinfo[NI_MAXSERV];
char *ip = "127.0.0.1";
u_short port = 2015;
int backlog = 100;
int i = 0;
DWORD thread_id;
u_long ul = 1;
int optval = 1;
LINGER lng;
wVersionRequested = MAKEWORD(2,2);
err = WSAStartup(wVersionRequested, &wsaData);
if(err != 0){
printf("WSAStartup failed with error:%d\n", err);
return 1;
}
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){
printf("Could not find a usable version of winsock.dll\n");
WSACleanup();
return 1;
}
hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
if(hCompletionPort == NULL){
printf("CreateIoCompletionPort() failed with error: %d\n", GetLastError());
WSACleanup();
return 1;
}
GetSystemInfo(&system_info);
// create count of cpu * 2 + 1 threads.
for(i = 0; i < system_info.dwNumberOfProcessors * 2 + 1; i++){
HANDLE thread_handle = CreateThread(NULL, 0, svr_worker_thread, hCompletionPort, 0, &thread_id);
if(thread_handle == NULL){
err = GetLastError();
printf("CreateThread() failed with error: %d\n", err);
return 1;
}
CloseHandle(thread_handle);
}
sckListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sckListener == INVALID_SOCKET){
printf("socket() failed with error : %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
sagni.sin_family = AF_INET;
sagni.sin_addr.s_addr = inet_addr(ip);
sagni.sin_port = htons(port);
lng.l_linger = 0;
lng.l_onoff = 1;
setsockopt(sckListener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&optval, sizeof(optval));
setsockopt(sckListener, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval));
setsockopt(sckListener, SOL_SOCKET, SO_DONTLINGER, (char *)&optval, sizeof(optval));
setsockopt(sckListener, SOL_SOCKET, SO_LINGER, (char *)&lng, sizeof(lng));
err = bind(sckListener, (struct sockaddr *)&sagni, sizeof(struct sockaddr));
if(err == SOCKET_ERROR){
printf("bind() failed with error: %d\n", WSAGetLastError());
closesocket(sckListener);
WSACleanup();
return 1;
}
err = listen(sckListener, backlog);
if(err == SOCKET_ERROR){
printf("listen() faield with error: %d\n", WSAGetLastError());
closesocket(sckListener);
WSACleanup();
return 1;
}
printf("Listening...\n");
while(1){
HANDLE hWorkPort;
DWORD recvBytes = 0, dwFlags = 0;
lp_per_data_key key;
lp_per_io_operation_data data;
sckAccepted = accept(sckListener, (struct sockaddr *)&saclient, &iclientSize);
if(sckAccepted == INVALID_SOCKET){
printf("WSAAccept() failed with error: %d\n", WSAGetLastError());
continue;
}
iConnected++;
printf("%s:%d connected. %d\n", inet_ntoa(saclient.sin_addr), ntohs(saclient.sin_port), iConnected);
if(ioctlsocket(sckAccepted, FIONBIO, (unsigned long *)&ul) == SOCKET_ERROR){
printf("ioctlsocket() failed, info:%d\n", WSAGetLastError());
closesocket(sckAccepted);
continue;
}
key = (lp_per_data_key)GlobalAlloc(GPTR, sizeof(per_data_key));
if(key == NULL){
printf("GlobalAlloc() failed with error: %d\n", GetLastError());
closesocket(sckListener);
WSACleanup();
return 1;
}
key->sock = sckAccepted;
hWorkPort = CreateIoCompletionPort((HANDLE)sckAccepted, hCompletionPort, (DWORD)key, 0);
if(hWorkPort == NULL){
printf("CreateIoCompletionPort() failed with error: %d\n", GetLastError());
closesocket(sckAccepted);
continue;
}
data = (lp_per_io_operation_data)GlobalAlloc(GPTR, sizeof(per_io_operation_data));
if(data == NULL){
printf("GlobalAlloc() failed with error: %d\n", GetLastError());
closesocket(sckListener);
WSACleanup();
return 1;
}
ZeroMemory(&(data->overlapped), sizeof(OVERLAPPED));
data->send_bytes = 0;
data->recv_bytes = 0;
data->databuf.len = SVR_DATA_BUF_SIZE;
data->databuf.buf = data->buffer;
data->nOp = OP_READ;
WSARecv(sckAccepted, &(data->databuf), 1, &recvBytes, &dwFlags, &(data->overlapped), NULL);
}
closesocket(sckListener);
WSACleanup();
return 0;
}
DWORD WINAPI svr_worker_thread(LPVOID lpParam)
{
HANDLE hCompletionPort = (HANDLE)lpParam;
DWORD bytesOfTransports;
lp_per_data_key key;
lp_per_io_operation_data data;
int err;
DWORD dwThreadId = GetCurrentThreadId();
while(TRUE){
BOOL succ = GetQueuedCompletionStatus(hCompletionPort, &bytesOfTransports,
(LPDWORD)&key, (LPOVERLAPPED *)&data, WSA_INFINITE);
if(succ == 0){
printf("GetQueuedCompletionStatus() failed with error: %d\n", GetLastError());
closesocket(key->sock);
GlobalFree(key);
GlobalFree(data);
continue;
}
if(bytesOfTransports == 0){
iConnected--;
printf("client socket was closed. %d\n", iConnected);
closesocket(key->sock);
GlobalFree(key);
GlobalFree(data);
continue;
}
switch(data->nOp){
case OP_READ:
{
}
break;
case OP_WRITE:
{
}
break;
default:
continue;
}
}
}
然后使用Python写了个简单的客户端,该客户端唯一做的事情就是不断的连接服务器,并不发送和接收数据。代码如下:
import thread
import socket
import sys
import time
host = '127.0.0.1'
port = 2015
msgcount = 1000
def thread_func(count):
socks = []
for i in range(0, count):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
except:
print 'Cant create more socket.\n'
continue
try:
sock.connect((host, port))
except:
print 'Cant connect server.\n'
continue
socks.append(sock)
time.sleep(5)
for s in socks:
s.close()
def main(c):
thread_func(c)
if __name__ == "__main__":
main(int(sys.argv[1]))
input()