比如说WSARecv的缓冲长度是10字节,IOCP开了2+线程,线程逻辑是把接收到的数据显示出来。而一次发来90字节,那么首先会启动一个线程处理前10个字节。 用代码实验结果,单核机器,XP系统,结果是:即便有线程被挂起,同一socket的数据处理也不会出现并发线程的情况,依然是串行的,数据被处理顺序依然没变!
代码如下,欢迎大家在不同机器不同系统上测试并反馈结果:
服务端:#include "stdafx.h"
#include <stdlib.h>
#include <WINSOCK2.H>
#include <stdio.h>
#define MSGSIZE 10
{
RECV_POSTED
}OPERATION_TYPE;
{
WSAOVERLAPPED overlap;
WSABUF Buffer;
char szMessage[MSGSIZE];
DWORD NumberOfBytesRecvd;
DWORD Flags;
OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
{
RECV_POSTED
}OPERATION_TYPE;
{
WSAOVERLAPPED overlap;
int BufCount;
WSABUF Buffer;
char szMessage[MSGSIZE];
//char szMsgBuf[MSGSIZE];
DWORD NumberOfBytesRecvd;
DWORD Flags;
OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
{
WSADATA wsaData;
SOCKET sListen, sClient;
SOCKADDR_IN local, client;
DWORD i, dwThreadId;
int iaddrSize = sizeof(SOCKADDR_IN);
HANDLE CompletionPort = INVALID_HANDLE_VALUE;
SYSTEM_INFO systeminfo;
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
WSAStartup(0x0202, &wsaData);
CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
GetSystemInfo(&systeminfo);
for (i = 0; i < systeminfo.dwNumberOfProcessors*2+2; i++)
{
CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
}
// Create listening socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
listen(sListen, 3);
{
// Accept a connection
sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
// Associate the newly arrived client socket with completion port
CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient, 0);
// Launch an asynchronous operation for new arrived connection
lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(PER_IO_OPERATION_DATA));
//lpPerIOData->BufCount = 0;
lpPerIOData->Buffer.len = MSGSIZE;
lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
lpPerIOData->OperationType = RECV_POSTED;
WSARecv(sClient,
&lpPerIOData->Buffer,
1,
&lpPerIOData->NumberOfBytesRecvd,
&lpPerIOData->Flags,
&lpPerIOData->overlap,
NULL);
}
CloseHandle(CompletionPort);
closesocket(sListen);
WSACleanup();
return 0;
}
{
HANDLE CompletionPort=(HANDLE)CompletionPortID;
DWORD dwBytesTransferred;
SOCKET sClient;
LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
{
GetQueuedCompletionStatus(
CompletionPort,
&dwBytesTransferred,
(unsigned long *)&sClient,
(LPOVERLAPPED *)&lpPerIOData,
INFINITE);
if (dwBytesTransferred == 0xFFFFFFFF)
{
return 0;
}
if (lpPerIOData->OperationType == RECV_POSTED)
{
if (dwBytesTransferred == 0)
{
// Connection was closed by client
closesocket(sClient);
HeapFree(GetProcessHeap(), 0, lpPerIOData);
}
else
{
lpPerIOData->szMessage[dwBytesTransferred] = '\0';
printf("iocp thread %d get %d bytes\n",GetCurrentThreadId(),dwBytesTransferred);
int t = 10 - atoi(lpPerIOData->szMessage+9);
printf("t=%d\n",t);
Sleep(t*1000);
send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0);
// Launch another asynchronous operation for sClient
memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
lpPerIOData->Buffer.len = MSGSIZE;
lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
lpPerIOData->OperationType = RECV_POSTED;
WSARecv(sClient,
&lpPerIOData->Buffer,
1,
&lpPerIOData->NumberOfBytesRecvd,
&lpPerIOData->Flags,
&lpPerIOData->overlap,
NULL);
}
}
}
return 0;
}
#include "stdafx.h"
#include <WINSOCK2.H>
#include <stdio.h>
#define SERVER_ADDRESS "127.0.0.1"
#define PORT 5150
#define MSGSIZE 90
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
SOCKET sClient;
SOCKADDR_IN server;
char szMessage[MSGSIZE];
int ret;
// Initialize Windows socket library
WSAStartup(0x0202, &wsaData);
// Create client socket
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Connect to server
memset(&server, 0, sizeof(SOCKADDR_IN));
server.sin_family = AF_INET;
server.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);
server.sin_port = htons(PORT);
connect(sClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));
while (TRUE)
{
printf("Send:");
gets(szMessage);
// Send message
send(sClient, szMessage, strlen(szMessage), 0);
// Receive message
ret = recv(sClient, szMessage, MSGSIZE, 0);
szMessage[ret] = '\0';
printf("Received [%d bytes]: '%s'\n", ret, szMessage);
}
// Clean up
closesocket(sClient);
WSACleanup();
return 0;
}
线程内的Sleep时间由数据包内的字符指定,所以字符数据数量应该为10的整数倍,第10个字符是数字。我发送的是111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999