WinSocket模型的探讨——Overlapped模型(一)

2024-01-05 19:08

本文主要是介绍WinSocket模型的探讨——Overlapped模型(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

重叠模型是Windows里一种重要的 I/O 模型,可以有效率的实现一些 I/O 操作,譬如文件读写、Socket读写等,在这里我们一起来研究一下重叠模型,看看它究竟为何方神圣。

这篇文章分为以下几部分来说明:

  1. 重叠模型的概念
  2. 容易碰到的一些问题
  3. 重叠模型的基本编程方法(accept 和 AcceptEx )
  4. 突破64个事件的等待限制
  5. 例程

好了,下面就让我们一起来学习一下重叠模型。

1、概念

对于重叠模型的概念,大家都各有说法,以我自己的角度来说,我觉得重叠其实就是一种异步处理的说法,一边向socket 投递操作,而在另一边等待结果的完成,两边互不相干,我想这就是重叠的概念。其实这个概念也不用深究,我们还是来看看如何使用重叠模型。

2、容易碰到的一些问题

为什么对Socket投递第一个WSARecv()出错并且返回错误为10045?

这个问题都是因为这个 WSARecv 的lpFlags参数引起的,这个参数一定要设置为0,而不是直接写为0,
 如果直接写为0,则会出现10014错误。要这样写:

DWORD dwFlags=0;
WSARecv(sClient, &as.m_wsaBuf, as.m_wsaBuf.len, &dwRecvBytes, &dwFlags, &as.m_overlapped, NULL);

为什么找不到AccepctEx的声明和定义?

要使用 AcceptEx,必须加上下面两句

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

AcceptEx 的第四个参数有什么用?

  AcceptEx 的第四个参数,指定建立连接的时候,接收客户端传过来的多少字节的数据,可以用来做验证客户端的字符串,如果这个参数为0,则AcceptEx在接受客户端连接后会立刻完成,不会接受任何客户端的数据。如果不为0,则接收客户端传过来的指定字节的数据。但这样会出现恶意连接连进来的时候,不发送任何数据,就导致了SERVER出错。

为什么程序只能收到连接,收不到从Socket传过来的数据?

程序不停的收到连接,但传过来的数据都不显示,这个问题的解决方法是:把事件数组的第一个设为和侦听Socket (sListen) 对应的事件,就是说第一个事件是给侦听Socket的,那在接受到连接后,要重新设置一下第一个事件:WSASetEvent(EventArray[0]); 以便工作线程可以继续往下走去服务其他事件。这里只知道要这样做,但具体原因不太清楚,希望高人能指点一下!

程序接收到的数据在哪里?

在使用WSARecv的时候,我们就已经传了一个WSABUF类型的变量进去,那在后面调用WSAGetOverlappedResult 之后,之前的WSABUF中的buf就是取得数据的地方,这些更仔细的说明建议大家看看Piggy的《手把手教你写 Overlapped 模型》,这篇文章对函数进行了比较详细的介绍。

3、基本编程方法:使用重叠模型,就要注意以下的一些函数。

WSASocket、AcceptEx、WSACreateEvent、WSAWaitForMultipleEvents、WSAResetEvent、WSAGetOverlappedResult、WSARecv、WSASetEvent。具体使用方法各位可以看下面的例子或者查一下MSDN,里面有介绍。

首先我们先看看重叠模型的使用步骤:

  • 创建一个带Overlapped标志的Socket句柄(其实也可以是文件句柄);
  • 准备好一个与这个Socket句柄对应的Overlapped对象,并准备好事件句柄,以便让后面的WaitForXXX函数使用
  • 使用支持Overlapped的函数对上面的句柄作操作(向这个句柄投递请求),这些函数都有一个共同点,就是它们都有个参数是Overlapped类型的。例如AcceptEx、WriteFile、WSARecv等;
  • 在一个循环或者一个线程中使用WSAWaitForMultipleEvents来等待事件的发生;
  • 事件发生后,使用WSAGetOverlappedResult 来取得对应的结果并做处理。
  • 继续向句柄投递操作请求(向它发东西啊,收东西啊等等的!)。

到这里,给大家一个建议,对着上面的步骤写写练习,要看代码网上一堆,真要理解,还是得亲自动手才行,编程技术,动手是硬道理!

好,我们一步步来看看代码片断,对上面的步骤有点理性的认识才行。

创建带Overlapped标志的Socket句柄:

 SOCKET sListen;
 if((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
 {
  OutErr("WSASocket error!");  
  return;
 }

准备好一个Overlapped对象,还有hEvent事件:

 // 准备好一个给 listenSocket 使用的 Overlapped
 ZeroMemory(&ListenOverlapped, sizeof(OVERLAPPED));
 ListenOverlapped.hEvent = WSACreateEvent();

对这个句柄投递操作,这是一个侦听句柄,当然就是给他投递AcceptEx操作:

 AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, 0, 
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes, 
    &ListenOverlapped);

在一个循环或者线程中等待事件的完成,并取得结果做处理:

 while(TRUE)
 {
  // 等待侦听事件的发生
  nIdx = WSAWaitForMultipleEvents(1, &ListenOverlapped.hEvent, FALSE, INFINITE, FALSE);

  // 取得这次Overlapped的结果
  WSAGetOverlappedResult(sListen, &ListenOverlapped, &dwTransferred, FALSE, &dwFlags);

  cout << sClient << " 连接上来了!" << endl;
  cout << "连接总数为:" << ++g_nClientCnt << endl;
  // 重置一下事件
  WSAResetEvent(ListenOverlapped.hEvent);

  // 再重新向ListenSocket投递一个AcceptEx请求
  sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
  ZeroMemory(szAcceptBuffer, 2*(sizeof(SOCKADDR_IN) + 16) );
  AcceptEx(sListen, sClient, (PVOID) szAcceptBuffer, dwRecvData, 
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes, 
    &ListenOverlapped);
 }

上面几步,基本上实现了AcceptEx的接受连接的整个过程,如果你都能看懂,那下面的例子程序也难不倒你了!

 4、突破64个事件的等待限制

虽说使用完成端口可以不受64个事件的限制,但我们这里既然是抱着钻研的态度,那总是要试一试的。

64个事件的等待限制是由Windows规定的,如果超过了64个,则WSAWaitForMultipleEvents函数会报错,错误码为87。要突破此限制的最基本的思路就是当线程等待的事件数超过了64,则新开线程来等待,把事件以64个为一个区,则0区、1区、2区。。。每区有64个事件,每个线程服务于一个区。这里要注意的是每个线程中的WaitForMultipleEvents要小心事件的个数、事件的起始位。详细的可以看下面的例子。

5、例程,下面的程序都是一个cpp文件拷贝过去就可以直接编译了。

 /*

  Server1: 使用 accept 来得到连接,并使用ProcessIO来处理结果,这里使用64个事件为一区,一个线程服务一个区的方法来突破了
  64个事件的限制。

*/

#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

#include <iostream>
using namespace std;

 

#define PORT 5050
#define DATA_BUFSIZE 8192
#define DYL_MAXIMUM_WAIT_EVENTS 1024

#define OutErr(a) cout << (a) << endl /
      << "出错代码:" << WSAGetLastError() << endl /
      << "出错文件:" << __FILE__ << endl  /
      << "出错行数:" << __LINE__ << endl /

#define OutMsg(a) cout << (a) << endl;

 

void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  OutErr("WSAStartup()");
 }
}

SOCKET BindServer(int nPort)
{
 // 创建socket 
 SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

 // 绑定端口
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
 {
  OutErr("bind Failed!");
  return NULL;
 }

 // 设置监听队列为200
 if(listen(sServer, WSA_MAXIMUM_WAIT_EVENTS) != 0)
 {
  OutErr("listen Failed!");
  return NULL;
 }
 return sServer;
}


DWORD EventTotal = 0;

WSAEVENT EventArray[DYL_MAXIMUM_WAIT_EVENTS];

typedef struct _SOCKET_INFORMATION {
   CHAR Buffer[DATA_BUFSIZE];
   WSABUF DataBuf;
   SOCKET Socket;
   WSAOVERLAPPED Overlapped;
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

LPSOCKET_INFORMATION SocketInfoArray[DYL_MAXIMUM_WAIT_EVENTS];

void CreateSocketInfo(SOCKET sClient)
{
 //if(EventTotal%WSA_MAXIMUM_WAIT_EVENTS)
 // EventTotal++;
 SocketInfoArray[EventTotal] = new SOCKET_INFORMATION();
 SocketInfoArray[EventTotal]->Socket = sClient;
 ZeroMemory(&SocketInfoArray[EventTotal]->Overlapped, sizeof(WSAOVERLAPPED));
 SocketInfoArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent();
 SocketInfoArray[EventTotal]->DataBuf.buf = SocketInfoArray[EventTotal]->Buffer;
 SocketInfoArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;

 memset(SocketInfoArray[EventTotal]->Buffer, 0, DATA_BUFSIZE);

 EventTotal++;
}

// 释放,注意N从0开始
void DestroySocketInfo(int n)
{
 delete SocketInfoArray[n];
 if( (n+1) != DYL_MAXIMUM_WAIT_EVENTS )
 {
  for(int i = n; i < EventTotal; i++)
  {
   SocketInfoArray[i] = SocketInfoArray[i + 1];
  }
 }
 EventTotal--;
}

DWORD WINAPI ProcessIO(LPVOID lpParam)
{
 int nSec = *((int*)lpParam);
 int nIdx = 0;
 DWORD BytesTransferred;
 DWORD Flags;
 int nTriggerCnt;

 while(1)
 {
  // 计算好事件数
  int nEvtCnt;
  if( EventTotal % WSA_MAXIMUM_WAIT_EVENTS == 0)
   nEvtCnt = 64;
  else
   nEvtCnt = EventTotal % WSA_MAXIMUM_WAIT_EVENTS;

  // 计算好事件数组的起始位置
  WSAEVENT *evtStart = EventArray + nSec*WSA_MAXIMUM_WAIT_EVENTS;
  if( (nIdx = WSAWaitForMultipleEvents(nEvtCnt, evtStart, FALSE, WSA_INFINITE, FALSE) ) == WSA_WAIT_FAILED )
  {
   OutErr("WSAWaitForMultipleEvents failed!");
   return 0; 
  }

  nTriggerCnt = nIdx - WSA_WAIT_EVENT_0;

  //!! 如果是0,则说明有新的客户要连接进来
  if ( nTriggerCnt == 0 )
  {
   WSAResetEvent(EventArray[0]);
   continue;
  }
  
  nTriggerCnt = nTriggerCnt + nSec*WSA_MAXIMUM_WAIT_EVENTS;
  LPSOCKET_INFORMATION SI = SocketInfoArray[nTriggerCnt];
  WSAResetEvent(EventArray[nTriggerCnt]);
  
  // 取得重叠操作的结果
  if(WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE || BytesTransferred == 0)
  {
   //OutErr("WSAGetOverlappedResult failed!");
   cout << "socket " << SI->Socket << " 断开!" << endl;
   DestroySocketInfo(nTriggerCnt);

   continue;
  }

  cout << SI->Socket << " 发送过来的数据:"  << SI->Buffer << endl;

  // 继续等待接收数据
  DWORD dwRecv;
  DWORD dwFlags = 0;

  if(WSARecv(SI->Socket, &(SI->DataBuf), 1, &dwRecv, &dwFlags, &(SI->Overlapped), NULL)  == SOCKET_ERROR)
  {
   if (WSAGetLastError() != ERROR_IO_PENDING)
   {
      OutErr("WSARecv error!");
      return 0;
   }   
  }
 }

 cout << "ProcessIO end!" << endl;

 return 0;
}

int g_nThreadCnt = 1; ///< 标示当前开了多少个线程来服务于

void main()
{
 InitWinsock();

 SOCKET sListen = BindServer(PORT);

 char szBuf[200];
 memset(szBuf, 0, 200);

 SOCKET sClient;
 if((sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
 {
  OutErr("WSASocket error!");  
  return;
 }

 if ((EventArray[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
 {
  OutErr("WSACreateEvent error!");
  return;
 }

 EventTotal = 1;

 int nSec = 0;
 CreateThread(NULL, 0, ProcessIO, (LPVOID)&nSec, 0, NULL);

 while(1)
 {
  sClient = accept(sListen, NULL, NULL);
  if(sClient == INVALID_SOCKET)
  {
   OutErr("accept faild!");
   return;
  }

  // 先判断是否能连接,是否超过了最大连接数

  if(EventTotal + 1 > DYL_MAXIMUM_WAIT_EVENTS)
  {
   cout << "Too many Client! closing socket" << sClient << endl;
   closesocket(sClient);
   continue;
  }

  cout << sClient << " 连接进来!" << endl;
  cout << "客户端连接数:" << EventTotal + 1 << endl;

  /// 这里要做身份验证,不准其他客户端连接上来

  // 判断是否需要分区
  if( int( EventTotal/WSA_MAXIMUM_WAIT_EVENTS ) + 1 > g_nThreadCnt)
  {
   nSec = int((EventTotal + 1)/WSA_MAXIMUM_WAIT_EVENTS);
   EventArray[nSec * WSA_MAXIMUM_WAIT_EVENTS] = EventArray[0];
   EventTotal++;
   CreateThread(NULL, 0, ProcessIO, (LPVOID)&nSec, 0, NULL);
   g_nThreadCnt++;
  }
  
  CreateSocketInfo(sClient);

  DWORD dwRecv;
  DWORD dwFlags = 0;
  int nCurCnt = EventTotal - 1;

  if( WSARecv(sClient, &(SocketInfoArray[EventTotal-1]->DataBuf), 1, &dwRecv, &dwFlags, 
     &(SocketInfoArray[EventTotal-1]->Overlapped), NULL) == SOCKET_ERROR )
  {
   if(WSAGetLastError() != WSA_IO_PENDING)
   {
    OutErr("WSARecv Error!");

    // 那就只能释放相关资源了  
    DestroySocketInfo(nCurCnt);
   }
  }

  if(int(EventTotal/WSA_MAXIMUM_WAIT_EVENTS) == 0)
   WSASetEvent(EventArray[0]);
  else
  {
   //!! 这里要设置一下0事件,让工作线程去服务事件数组里的其他事件
   for(int i = 0 ; i < int(EventTotal/WSA_MAXIMUM_WAIT_EVENTS); i++)
   {
    if (WSASetEvent(EventArray[i*WSA_MAXIMUM_WAIT_EVENTS]) == FALSE)
    {
     OutErr("WSASetEvent failed!");
     return;
    }
   }
  }
 }
 
}

/**
Server2.cpp: 使用了 AcceptEx 来处理连接
**/

/// 包含头文件
#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <mswsock.h>
#pragma comment(lib, "mswsock.lib")

#include <windows.h>

#include <iostream>
using namespace std;


/// 宏定义
#define PORT 5050
#define DATA_BUFSIZE 8192

#define OutErr(a) cout << (a) << endl /
      << "出错代码:" << WSAGetLastError() << endl /
      << "出错文件:" << __FILE__ << endl  /
      << "出错行数:" << __LINE__ << endl /

#define OutMsg(a) cout << (a) << endl;


/// 全局变量定义
DWORD EventTotal = 0;

// #define WSA_MAXIMUM_WAIT_EVENTS 100

WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];

typedef struct _SOCKET_INFORMATION {
   CHAR Buffer[DATA_BUFSIZE];
   WSABUF DataBuf;
   SOCKET Socket;
   WSAOVERLAPPED Overlapped;
} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

WSAOVERLAPPED ListenOverlapped;

LPSOCKET_INFORMATION SocketInfoArray[WSA_MAXIMUM_WAIT_EVENTS];

/// 全局函数定义
void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  OutErr("WSAStartup()");
 }
}

SOCKET BindServer(int nPort)
{
 // 创建socket 
 SOCKET sServer = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

 // 绑定端口
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 if(bind(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
 {
  OutErr("bind Failed!");
  return NULL;
 }

 // 设置监听队列为200
 if(listen(sServer, WSA_MAXIMUM_WAIT_EVENTS) != 0)
 {
  OutErr("listen Failed!");
  return NULL;
 }
 return sServer;
}

void CreateSocketInfo(SOCKET sClient)
{
 SocketInfoArray[EventTotal] = new SOCKET_INFORMATION;
 SocketInfoArray[EventTotal]->Socket = sClient;
 ZeroMemory(&SocketInfoArray[EventTotal]->Overlapped, sizeof(WSAOVERLAPPED));
 SocketInfoArray[EventTotal]->Overlapped.hEvent = EventArray[EventTotal] = WSACreateEvent();
 SocketInfoArray[EventTotal]->DataBuf.buf = SocketInfoArray[EventTotal]->Buffer;
 SocketInfoArray[EventTotal]->DataBuf.len = DATA_BUFSIZE;

 memset(SocketInfoArray[EventTotal]->Buffer, 0, DATA_BUFSIZE);

 EventTotal++;
}

// 释放,注意N从0开始
void DestroySocketInfo(int n)
{
 delete SocketInfoArray[n];
 if( (n+1) != WSA_MAXIMUM_WAIT_EVENTS )
 {
  for(int i = n; i < EventTotal; i++)
  {
   SocketInfoArray[i] = SocketInfoArray[i + 1];
  }
 }
 EventTotal--;
}


// 正文
void main()
{
 InitWinsock();

 SOCKET sListen, sClient;

 sListen = BindServer(PORT);

 // 准备好连接的SOCKET
 sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
 if(sClient == SOCKET_ERROR)
 {
  OutErr("WSASocket error!");
  return;
 }

 // 给这个Listen SOCKET分配一个Overlapped,可以把这个SOCKET 想象为其他的句柄,例如文件句柄
 ZeroMemory(&ListenOverlapped, sizeof(OVERLAPPED));
 if ((EventArray[EventTotal++] = ListenOverlapped.hEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
 {
  OutErr("WSACreateEvent failed");
  return;
 }

 // 投递一个 AcceptEx 请求,注意这里的AcceptBuffer参数是指:取得的第一块从CLIENT发来的数据
 // 这块数据包括:1、本地的地址 2、连接端的地址 3、这些地址是写在这个缓冲区的后面一段的(具体可再参看MSDN)
 CHAR AcceptBuffer[2 * (sizeof(SOCKADDR_IN) + 16)];
 DWORD dwRecvBytes;
 if (AcceptEx(sListen, sClient, (PVOID) AcceptBuffer, 0, 
  sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes, 
  &ListenOverlapped) == FALSE)
 {
  if (WSAGetLastError() != ERROR_IO_PENDING)
  {
   OutErr("AcceptEx failed");
   return;
  }
 }

 // 真正的工作开始了,在下面可以异步去处理Accept、WSARecv、WSASend等请求
 int nIdx;
 DWORD BytesTransferred;
 DWORD Flags;
 while(TRUE)
 {
  // 首先等待事件的发生
  if( (nIdx = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, INFINITE, FALSE)) == WSA_WAIT_FAILED )
  {
   OutErr("WSAWaitForMultipleEvents Error!");
   return;
  }

  // 处理连接
  if( (nIdx - WSA_WAIT_EVENT_0) == 0)
  {
   // 先看看什么结果,反正 WSAWaitForMultipleEvents 调用完成,就肯定是有某种事情发生了
   if(WSAGetOverlappedResult(sListen, &ListenOverlapped, &BytesTransferred, FALSE, &Flags) == FALSE)
   {
    OutErr("nIdx 为 0,WSAGetOverlappedResult error!");
    return;
   }
  
   if (EventTotal >= WSA_MAXIMUM_WAIT_EVENTS)
   {
    cout << "Too many connections - closing socket." << endl;
    closesocket(sClient);

    goto ReAccept;
   }
   else
   {
    // cout << AcceptBuffer << endl;
    cout << "socket " << sClient << " 连接进来" << endl;
    cout << "连接客户端总数 " << EventTotal << endl;

    // 创建相应的结构
    CreateSocketInfo(sClient);

    // 向刚进来的连接 投递一个WSARecv操作
    Flags = 0;
    if ( WSARecv(sClient, &(SocketInfoArray[EventTotal-1]->DataBuf), 1, &dwRecvBytes, &Flags, 
     &(SocketInfoArray[EventTotal-1]->Overlapped), NULL) == SOCKET_ERROR)
    {
     if (WSAGetLastError() != ERROR_IO_PENDING)
     {
      OutErr("WSARecv error!");
      return;
     }
    }
   }

ReAccept:
   // 再重新向ListenSocket投递一个AcceptEx请求
   sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
   if(sClient == INVALID_SOCKET )
   {
    OutErr("WSASocket Error!");
    return;
   }

   // 重置一下事件
   if(!WSAResetEvent(EventArray[0]))
   {
    OutErr("WSAResetEvent error!");
    return;
   }

   ZeroMemory(&ListenOverlapped, sizeof(OVERLAPPED));
   ListenOverlapped.hEvent = EventArray[0];
   
   if (AcceptEx(sListen, sClient, (PVOID) AcceptBuffer, 0,
    sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwRecvBytes, 
    &ListenOverlapped) == FALSE)
   {
    if (WSAGetLastError() != ERROR_IO_PENDING)
    {
     OutErr("AcceptEx Error!");
     return;
    }
   } 
   
   continue;
  }

  // 上面搞了一大通,终于可以开始处理真正的客户端请求了
  LPSOCKET_INFORMATION SI;
  SI = SocketInfoArray[nIdx - WSA_WAIT_EVENT_0];
  WSAResetEvent(SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]);

  // 取得结果
  if (WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE
   || BytesTransferred == 0)
  {
   cout << SI->Socket << " 断开" << endl;
   DestroySocketInfo(nIdx - WSA_WAIT_EVENT_0);
   continue;
  }   

  cout << SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]->Socket << " 发过来的数据:" << SocketInfoArray[nIdx - WSA_WAIT_EVENT_0]->Buffer << endl;

    // 再投递新的接收请求
  Flags = 0;
  if (WSARecv(SI->Socket, &(SI->DataBuf), 1, &dwRecvBytes, &Flags,
   &(SI->Overlapped), NULL) == SOCKET_ERROR)
  {
   if (WSAGetLastError() != ERROR_IO_PENDING)
   {
    OutErr("WSARecv() failed");
    return;
   }
  }  
 } 
}

 
/**
测试客户端

  Client1.cpp: 
  1、服务器断开重连接
  2、测试单个客户端连接对话

**/

#include <winsock2.h>
#pragma comment(lib, "WS2_32")

#include <iostream>
using namespace std;

#define PORT 5050

#define OutErr(a) cout << (a) << endl /
      << "出错代码:" << WSAGetLastError() << endl /
      << "出错文件:" << __FILE__ << endl  /
      << "出错行数:" << __LINE__ << endl /

#define OutMsg(a) cout << (a) << endl;

void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  OutErr("WSAStartup()");
 }
}

SOCKET ConnectServer(char *lpszServerIP, int nPort, ULONG NonBlock)
{
 SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //ioctlsocket(sServer, FIONBIO, &NonBlock);
 
 struct hostent *pHost = NULL;
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = inet_addr(lpszServerIP);

 
 // 如果给的是主机的名字而不是IP地址
 if(servAddr.sin_addr.s_addr == INADDR_NONE)
 {
  pHost = gethostbyname( lpszServerIP );
  if(pHost == NULL)
  {
   OutErr("gethostbyname Failed!");
   return NULL;
  }
  memcpy(&servAddr.sin_addr, pHost->h_addr_list[0], pHost->h_length);
 }

 int nRet = 0;
 nRet = connect(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr));
 if( nRet == SOCKET_ERROR )
 {
  OutErr("connect failed!");
  return NULL;
 }
  
 return sServer;
}


void main()
{

 InitWinsock();

connect:
 SOCKET sServer = ConnectServer("127.0.0.1", PORT, 0);


reSay:
 char szBuf[1024];
 memset(szBuf, 0, 1024);
 cout << "你要说:";
 cin >> szBuf; 

 if(send(sServer, szBuf, strlen(szBuf), 0) == SOCKET_ERROR)
  goto connect;
 else
  goto reSay;
}

 /*
测试客户端2
 Client2.cpp:测试多个客户端连接
  */

#include <winsock2.h>
#pragma comment(lib, "WS2_32")


#include <iostream>
using namespace std;

#define PORT 5050

#define OutErr(a) cout << (a) << endl /
      << "出错代码:" << WSAGetLastError() << endl /
      << "出错文件:" << __FILE__ << endl  /
      << "出错行数:" << __LINE__ << endl /

#define OutMsg(a) cout << (a) << endl;


// 全局函数定义
void InitWinsock()
{
 // 初始化WINSOCK
 WSADATA wsd;
 if( WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
 {
  OutErr("WSAStartup()");
 }
}

SOCKET ConnectServer(char *lpszServerIP, int nPort, ULONG NonBlock)
{
 SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //ioctlsocket(sServer, FIONBIO, &NonBlock);
 
 struct hostent *pHost = NULL;
 struct sockaddr_in servAddr;
 servAddr.sin_family = AF_INET;
 servAddr.sin_port = htons(nPort);
 servAddr.sin_addr.s_addr = inet_addr(lpszServerIP);

 
 // 如果给的是主机的名字而不是IP地址
 if(servAddr.sin_addr.s_addr == INADDR_NONE)
 {
  pHost = gethostbyname( lpszServerIP );
  if(pHost == NULL)
  {
   OutErr("gethostbyname Failed!");
   return NULL;
  }
  memcpy(&servAddr.sin_addr, pHost->h_addr_list[0], pHost->h_length);
 }

 int nRet = 0;
 nRet = connect(sServer, (struct sockaddr *)&servAddr, sizeof(servAddr));
 if( nRet == SOCKET_ERROR )
 {
  OutErr("connect failed!");
  return NULL;
 }
  
 return sServer;
}


void main()
{
 InitWinsock();

 SOCKET sServer[64];

 for(int i = 0; i < 63; i++)
 {
  sServer[i] = ConnectServer("127.0.0.1", PORT, 0);
  
  if(sServer[i] != NULL)
  {
   send(sServer[i], "hello", 6, 0);
  }
  Sleep(100);  
 }

 while(1)
 {
  Sleep(1000);
 }
}

 把上面的几个CPP文件都拷贝到自己的工程里就可以编译通过。这篇文章也暂时到这里为止,其实如果要做下去的事情非常多,例如封装好各种对象,用容器来管理,把重叠模型封装成一个固定框架,在外添加应用等等。。。但这段时间忙于项目,根本没时间再去钻研了。所以有时间的话,我会把钻研成果写在重叠模型的下篇文章中。另外,如果还有什么问题,欢迎各位提问探讨。

下篇文章的计划:

  1. 解决这篇文章没解决的问题;
  2. 重叠模型的完成例程;
  3. 应用重叠模型(包括实现登录、聊天室、传送文件等功能);
  4. 重叠模型在文件拷贝中的应用。。。  

这篇关于WinSocket模型的探讨——Overlapped模型(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/573872

相关文章

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

AI Toolkit + H100 GPU,一小时内微调最新热门文生图模型 FLUX

上个月,FLUX 席卷了互联网,这并非没有原因。他们声称优于 DALLE 3、Ideogram 和 Stable Diffusion 3 等模型,而这一点已被证明是有依据的。随着越来越多的流行图像生成工具(如 Stable Diffusion Web UI Forge 和 ComyUI)开始支持这些模型,FLUX 在 Stable Diffusion 领域的扩展将会持续下去。 自 FLU

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者