采用重叠I/O技术的Socket客户端代码

本文详细介绍了如何利用重叠I/O技术来构建一个高性能的Socket客户端。通过这种方式,可以实现非阻塞的I/O操作,提高并发处理能力。文章涵盖了重叠I/O的概念、Windows平台下的完成端口(IOCP)使用,以及如何结合线程池来处理异步事件。

 

#ifdef _DEBUG
#define GMT_ASSERT(exp)  assert(exp)
#else
#define GMT_ASSERT(exp)
#endif
#define PORT 5150
#define DATA_BUFSIZE 8192
// Overlapped event.
#define RECV_WSAEVENT 0
#define SEND_WSAEVENT 1
#define CTRL_IOTHREAD_WSAEVENT 2
#define WSAEVENT_COUNT 3
// Index of thread type.
#define SEND_THREAD  0
#define IO_THREAD  1
#define THREAD_COUNT 2
typedef struct _SOCKET_INFORMATION {
 char szIOBuffer[DATA_BUFSIZE];
 WSABUF DataBuf;
 WSAOVERLAPPED Overlapped;
 DWORD BytesSEND;
 DWORD BytesRECV;
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;
SOCKET ClientSocket;
DWORD EventTotal = 0;
HANDLE hThread[THREAD_COUNT];
WSAEVENT EventArray[WSAEVENT_COUNT];
LPSOCKET_INFORMATION g_pSIRecv = NULL;
LPSOCKET_INFORMATION g_pSISend = NULL;
void CloseClientSocket()
{
 if (INVALID_SOCKET != ClientSocket)
 {
  if (closesocket(ClientSocket) == 0)
   ClientSocket = INVALID_SOCKET;
 }
}
// Deliver recv operation.
int RecvData()
{
 if (NULL == g_pSIRecv || INVALID_SOCKET == ClientSocket)
  return FALSE;
 DWORD dwFlags = 0;
 g_pSIRecv->BytesRECV = 0;
 memset(g_pSIRecv->szIOBuffer, 0, sizeof(g_pSIRecv->szIOBuffer));
 g_pSIRecv->DataBuf.len = sizeof(g_pSIRecv->szIOBuffer)-1;
 int nRet = WSARecv(ClientSocket, &(g_pSIRecv->DataBuf), 1, &g_pSIRecv->BytesRECV, &dwFlags, &(g_pSIRecv->Overlapped), NULL);
 if (nRet == SOCKET_ERROR)
 {
  if (WSAGetLastError() != ERROR_IO_PENDING)
  {
   printf("WSARecv() failed with error %d/n", WSAGetLastError()); 
   CloseClientSocket();
   return FALSE;
  }
 }
 return TRUE;
}
// Deliver send operation.
BOOL SendData()
{
 if (NULL == g_pSIRecv || INVALID_SOCKET == ClientSocket)
  return FALSE;
 g_pSISend->BytesSEND = 0;
 strcpy(g_pSISend->szIOBuffer, "this is client data.");
 g_pSISend->DataBuf.len = strlen(g_pSISend->szIOBuffer);
 int nRet = WSASend(ClientSocket, &(g_pSISend->DataBuf), 1, &g_pSISend->BytesSEND, 0, &(g_pSISend->Overlapped), NULL);
 if (nRet == SOCKET_ERROR)
 {
  if (WSAGetLastError() != ERROR_IO_PENDING)
  {
   printf("WSASend() failed with error %d/n", WSAGetLastError());
   CloseClientSocket();
   return FALSE;
  }
 }
 return TRUE;
}
unsigned __stdcall SendThreadFunc( void* pArguments )
{
 printf("Send thread is running .../r/n");
 while(TRUE)
 {
  if (!SendData())
   break;
  Sleep(1000);
 }
 if (WSA_INVALID_EVENT != EventArray[2])
 {
  WSASetEvent(EventArray[2]);
  EventArray[2] = WSA_INVALID_EVENT;
 }
 printf("SendThreadFunc is over./r/n");
 return 0;
}
unsigned __stdcall IOThreadFunc( void* pArguments )
{
 printf("IO thread is running .../r/n");
 DWORD dwEventIndex = 0;
 DWORD dwFlags = 0;
 DWORD BytesTransferred = 0;
 LPSOCKET_INFORMATION pSI = NULL;
 // Process asynchronous WSASend, WSARecv requests.
 while(TRUE)
 {
  // Wait overlapped I/O event.
  dwEventIndex = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
  GMT_ASSERT(dwEventIndex < 3);
  if ( dwEventIndex == WSA_WAIT_FAILED)
  {
   printf("WSAWaitForMultipleEvents failed %d/n", WSAGetLastError());
   break;
  }
  WSAResetEvent(EventArray[dwEventIndex - WSA_WAIT_EVENT_0]);
  if (dwEventIndex - WSA_WAIT_EVENT_0 == RECV_WSAEVENT)
   pSI = g_pSIRecv; // Recv event is signaled.
  else if (dwEventIndex - WSA_WAIT_EVENT_0 == SEND_WSAEVENT)
   pSI = g_pSISend; // Send event is signaled.
  else if (dwEventIndex - WSA_WAIT_EVENT_0 == CTRL_IOTHREAD_WSAEVENT)
   break;    // IO thread Control event is signaled, exit.
  GMT_ASSERT(NULL != pSI);
  // I/O operation is completed.
  dwFlags = 0;
  BytesTransferred = 0;
  BOOL bRet = WSAGetOverlappedResult(ClientSocket, &(pSI->Overlapped), &BytesTransferred, TRUE, &dwFlags);
  if ( !bRet)
  {
   printf("WSAGetOverlappedResult error code: %d/r/n", WSAGetLastError());
   break;
  }
  if (0 == BytesTransferred)
  {
   printf("IO Bytes is 0, error code is %d /r/n", WSAGetLastError());
   break;
  }
  if (dwEventIndex - WSA_WAIT_EVENT_0 == 0)  // Recv data successfully.
  {
   printf("recv data: [%d]%s/r/n", BytesTransferred, pSI->szIOBuffer);
  }
  else if (dwEventIndex - WSA_WAIT_EVENT_0 == 1) // Send data successfully.
  {
//   printf("send data successfully ... /r/n");
  }
  // Deliver recv operation.
  if (!RecvData()) break;
 }
 CloseClientSocket();
 for (int i=0; i<EventTotal; i++)
 {
  if (WSA_INVALID_EVENT != EventArray[i])
  {
   WSACloseEvent(EventArray[i]);
   EventArray[i] = WSA_INVALID_EVENT;
  }
 }
 delete g_pSIRecv;
 g_pSIRecv = NULL;
 delete g_pSISend;
 g_pSISend = NULL;
 WSACleanup();
 printf("IOThread is over./r/n");
 return 0;
}
BOOL InitializeSocket(LPCTSTR lpServerIp, DWORD dwServerPort)
{
 WSADATA wsaData;
 SOCKET AcceptSocket;
 int nRet;
 if ((nRet = WSAStartup(0x0202,&wsaData)) != 0)
 {
  printf("WSAStartup failed with error %d/n", nRet);
  WSACleanup();
  return FALSE;
 }
 ClientSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
 if (INVALID_SOCKET == ClientSocket)
 {
  printf("WSASocket call failed with error: %ld/n", WSAGetLastError());
  WSACleanup();
  return FALSE;
 }
 // Connect to server.
 SOCKADDR_IN AddrServer;
 AddrServer.sin_family = AF_INET;
 AddrServer.sin_addr.s_addr = inet_addr(lpServerIp);
 AddrServer.sin_port = htons(dwServerPort);
 nRet = connect( ClientSocket, (struct sockaddr*)(&AddrServer), sizeof(SOCKADDR_IN) );
 if ( nRet == SOCKET_ERROR)
 {
  printf( "Failed to connect. Error Code: %d/n", WSAGetLastError());
  WSACleanup();
  return FALSE;
 }
 else
 {
  printf("connect to server successfully./r/n");
 }
 for (EventTotal=0; EventTotal<WSAEVENT_COUNT; EventTotal++)
 {
  EventArray[EventTotal] = WSACreateEvent();
  if (EventArray[EventTotal] == WSA_INVALID_EVENT)
  {
   printf("WSACreateEvent failed with error %d/n", WSAGetLastError());
   WSACleanup();
   return FALSE;
  }
 }
 g_pSIRecv = new SOCKET_INFORMATION;
 ZeroMemory(&(g_pSIRecv->Overlapped), sizeof(WSAOVERLAPPED));
 g_pSIRecv->Overlapped.hEvent = EventArray[RECV_WSAEVENT];
 g_pSIRecv->DataBuf.len = sizeof(g_pSIRecv->szIOBuffer)-1;
 g_pSIRecv->DataBuf.buf = g_pSIRecv->szIOBuffer;
 memset(g_pSIRecv->szIOBuffer, 0, sizeof(g_pSIRecv->szIOBuffer));
 g_pSISend = new SOCKET_INFORMATION;
 ZeroMemory(&(g_pSISend->Overlapped), sizeof(WSAOVERLAPPED));
 g_pSISend->Overlapped.hEvent = EventArray[SEND_WSAEVENT];
 g_pSISend->DataBuf.len = sizeof(g_pSISend->szIOBuffer)-1;
 g_pSISend->DataBuf.buf = g_pSISend->szIOBuffer;
 memset(g_pSISend->szIOBuffer, 0, sizeof(g_pSISend->szIOBuffer));
 // Create a thread to service overlapped requests
 unsigned threadID;
 printf( "Creating 2 thread.../n" );
 // Create the IO thread.
 hThread[IO_THREAD] = (HANDLE)_beginthreadex( NULL, 0, &IOThreadFunc, NULL, 0, &threadID );
 if (hThread[IO_THREAD] == NULL)
 {
  printf("CreateThread failed with error %d/n", GetLastError());
  WSACleanup();
  return FALSE;
 }
 // Create the send thread.
 hThread[SEND_THREAD] = (HANDLE)_beginthreadex( NULL, 0, &SendThreadFunc, NULL, CREATE_SUSPENDED, &threadID );
 if (hThread[SEND_THREAD] == NULL)
 {
  printf("CreateThread failed with error %d/n", GetLastError());
  WSACleanup();
  return FALSE;
 }
 CloseHandle(hThread[IO_THREAD]);
 CloseHandle(hThread[SEND_THREAD]);
 return TRUE;
}
//
// @ret success 0
//      fail -1
OVERLAPPEDIOCLIENT_API int SendData(LPCTSTR lpData)
{
 int nEndRet = 0;
 if (NULL == lpData)
 {
  return -1;
 }
 // callee count.
 static DWORD dwSndCount = 0;
 if (0 == dwSndCount)
 {
   ResumeThread(hThread[IO_THREAD]);
   ResumeThread(hThread[SEND_THREAD]);
 }
 dwSndCount++;
 if (NULL == g_pSISend)
  return -1;
 memset(g_pSISend->szIOBuffer, 0, sizeof(g_pSISend->szIOBuffer));
 strcpy(g_pSISend->szIOBuffer, lpData);
 g_pSISend->DataBuf.len = strlen(g_pSISend->szIOBuffer);
 return SendData();
}

// Set sending file and recving file, listen ip and port.
// @para lpRecvFile  recv file path.
//   lpServerIp  NULL any ip or specified ip.
//   dwPort   listen port.
//   dwRecvInterval Recv operation time interval.
// @ret -1 fail.
//  0 success
OVERLAPPEDIOCLIENT_API int SetClient(LPCTSTR lpRecvFile, LPCTSTR lpServerIp, DWORD dwServerPort, DWORD dwRecvInternal)
{
 GMT_ASSERT(lpRecvFile != NULL);
 GMT_ASSERT(lpServerIp != NULL);
 return InitializeSocket(lpServerIp, dwServerPort);
}
 
 
// using
 dllmodule=LoadLibrary("OverLappedIOClient.dll");
 if (NULL == dllmodule)
 {
  DWORD dwErr = GetLastError();
  cout <<"LoadLibrary failed!" << "error code:" << dwErr << endl;
 }  
 PFSETCLIENT pfSetClient = (PFSETCLIENT)GetProcAddress(dllmodule,"SetClient");
 if (NULL == pfSetClient)
 {
  DWORD dwErr = GetLastError();
  cout <<"GetProcAddress(dllmodule,SetClient) failed! "<< "error code:" << dwErr << endl;
  FreeLibrary(dllmodule);
  return -1;
 }
 LPCTSTR lpRecvFile = "D://VC2005Project//ConselCodeTest//debug//ClientRecvFile.txt";
 if (!pfSetClient(lpRecvFile, "127.0.0.1", 5150, 2000))
 {
  FreeLibrary(dllmodule);
  return -1;
 }
 PSEND pfSend = (PSEND)GetProcAddress(dllmodule,"SendData");
 if (NULL == pfSend)
 {
  DWORD dwErr = GetLastError();
  cout <<"GetProcAddress(dllmodule,Send) failed!"<< "error code:" << dwErr << endl;
  FreeLibrary(dllmodule);
  return -1;
 }

 int nCount = 0;
 string str;
 DWORD dwSend;
 str = "this is test data";
 while (TRUE)
 {
  LPCTSTR lpData = str.data();
  if (str == "stop")
  {
   lpData = NULL;
   dwSend = 0;
  }
  else
   dwSend = strlen(lpData);
  if (-1 == pfSend(lpData)) break;
  Sleep(1000);
 }
 str.clear();
 FreeLibrary(dllmodule);
 return 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值