`
agn93agn
  • 浏览: 24120 次
社区版块
存档分类
最新评论

遇到10041错误

 
阅读更多

遇到10041错误
2010年08月03日
  今天在一段代码中调用 WSARecv 函数时老遇到 10041 错误,工具表示:在套接字函数调用中指定的一个协议不支持请求的套接字类型的语法。 
  于是我想是不是缓冲区指针不对?但经过检查不是这个问题。后来网上找到了错误的原因:函数的第5个参数如果直接写为0,则会出现10014错误。要这样写: DWORD dwFlags=0; 
  以下是原文:地址http://www.joyvc.cn/NetworkAndCommunication/Networ kAndCommunication00210.html
  WinSocket模型的探讨--Overlapped模型(一) 原作者:一雨田    源出处:CSDN    发布者:施昌权    发布类型:转载    发布日期:2009-03-21
  重叠模型是Windows里一种重要的 I/O 模型,可以有效率的实现一些 I/O 操作,譬如文件读写、Socket读写等,在这里我们一起来研究一下重叠模型,看看它究竟为何方神圣。
  这篇文章分为以下几部分来说明: 重叠模型的概念 
  容易碰到的一些问题 
  重叠模型的基本编程方法(accept 和 AcceptEx ) 
  突破64个事件的等待限制 
  例程 
  好了,下面就让我们一起来学习一下重叠模型。
  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 
  #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 等待限制
  虽说使用完成端口可以不受64个事件的限制,但我们这里既然是抱着钻研的态度,那总是要试一试的。
  64个事件的等待限制是由Windows规定的,如果超过了64个,则WSAWaitForMultipleEvents函数会报错,错误码为87。要突破此限制的最基本的思路就是当线程等待的事件数超过了64,则新开线程来等待,把事件以64个为一个区,则0区、1区、2区。。。每区有64个事件,每个线程服务于一个区。这里要注意的是每个线程中的WaitForMultipleEvents要小心事件的个数、事件的起始位。详细的可以看下面的例子。
  5、例程,下面的程序都是一个cpp文件拷贝过去就可以直接编译了。
  /*
  Server1: 使用 accept 来得到连接,并使用ProcessIO来处理结果,这里使用64个事件为一区,一个线程服务一个区的方法来突破了
  64个事件的限制。
  */
  #include 
  #pragma comment(lib, "WS2_32")
  #include 
  #pragma comment(lib, "mswsock.lib")
  #include 
  using namespace std;
  #define PORT 5050
  #define DATA_BUFSIZE 8192
  #define DYL_MAXIMUM_WAIT_EVENTS 1024
  #define OutErr(a) cout 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)) 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 结果
  if(WSAGetOverlappedResult(SI->Socket, &(SI->Overlapped), &BytesTransferred, FALSE, &Flags) == FALSE || BytesTransferred == 0)
  {
  //OutErr("WSAGetOverlappedResult failed!");
  cout socket " Socket Socket 发送过来的数据:"  Buffer 等待接收数据
  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 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 socket"  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 
  #pragma comment(lib, "WS2_32")
  #include 
  #pragma comment(lib, "mswsock.lib")
  #include 
  #include 
  using namespace std;
  /// 宏定义
  #define PORT 5050
  #define DATA_BUFSIZE 8192
  #define OutErr(a) cout 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)) 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 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 socket." socket " 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 Socket Socket Buffer 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 
  #pragma comment(lib, "WS2_32")
  #include 
  using namespace std;
  #define PORT 5050
  #define OutErr(a) cout 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 > szBuf; 
  if(send(sServer, szBuf, strlen(szBuf), 0) == SOCKET_ERROR)
  goto connect;
  else
  goto reSay;
  }
  /*
  测试客户端2
  Client2.cpp:测试多个客户端连接
  */
  #include 
  #pragma comment(lib, "WS2_32")
  #include 
  using namespace std;
  #define PORT 5050
  #define OutErr(a) cout 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文件都拷贝到自己的工程里就可以编译通过。这篇文章也暂时到这里为止,其实如果要做下去的事情非常多,例如封装好各种对象,用容器来管理,把重叠模型封装成一个固定框架,在外添加应用等等。。。但这段时间忙于项目,根本没时间再去钻研了。所以有时间的话,我会把钻研成果写在重叠模型的下篇文章中。另外,如果还有什么问题,欢迎各位提问探讨。
  下篇文章的计划: 解决这篇文章没解决的问题; 
  重叠模型的完成例程; 
  应用重叠模型(包括实现登录、聊天室、传送文件等功能); 
  重叠模型在文件拷贝中的应用。。。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics