实验IOCP多线程是否会导致TCP乱序

来源:互联网 发布:vue数据交互 编辑:程序博客网 时间:2024/05/19 04:04
         由于IOCP是多线程的,所以有这样的疑问:如果WSARecv缓冲的尺寸明显小于一次到达的数据尺寸,而且线程后续代码里有挂起的情况,那么是否会并发线程处理同一socket的接收数据,进而因线程不同步导致后续数据处理在时序上乱序。
         比如说WSARecv的缓冲长度是10字节,IOCP开了2+线程,线程逻辑是把接收到的数据显示出来。而一次发来90字节,那么首先会启动一个线程处理前10个字节。         用代码实验结果,单核机器,XP系统,结果是:即便有线程被挂起,同一socket的数据处理也不会出现并发线程的情况,依然是串行的,数据被处理顺序依然没变!
         代码如下,欢迎大家在不同机器不同系统上测试并反馈结果:
服务端:#include "stdafx.h"
#include <stdlib.h>

#include <WINSOCK2.H>
#include <stdio.h>
#define PORT    5150
#define MSGSIZE 10
#pragma comment(lib, "ws2_32.lib")
typedef enum
{
 RECV_POSTED
}OPERATION_TYPE;
typedef struct
{
 WSAOVERLAPPED  overlap;
 WSABUF         Buffer;
 char           szMessage[MSGSIZE];
 DWORD          NumberOfBytesRecvd;
 DWORD          Flags;
 OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;
DWORD WINAPI WorkerThread(LPVOID);
/*typedef enum
{
  RECV_POSTED
}OPERATION_TYPE;
typedef struct
{
 
 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;
DWORD WINAPI WorkerThread(LPVOID);*/
int main()
{
  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;
  // Initialize Windows Socket library
  WSAStartup(0x0202, &wsaData);
  // Create completion port
  CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  // Create worker thread
  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);
  // Bind
  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
  listen(sListen, 3);
  while (TRUE)
  {
    // 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);
  }
PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
 CloseHandle(CompletionPort);
 closesocket(sListen);
 WSACleanup();
 return 0;
}
DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
  HANDLE                  CompletionPort=(HANDLE)CompletionPortID;
  DWORD                   dwBytesTransferred;
  SOCKET                  sClient;
  LPPER_IO_OPERATION_DATA lpPerIOData = NULL;
  while (TRUE)
  {
    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 
,经过服务器IOCP以后返回显示的顺序没乱。          

0 0
原创粉丝点击