现在的位置: 首页 > 综合 > 正文

Windows网络编程杂谈

2012年09月25日 ⁄ 综合 ⁄ 共 3944字 ⁄ 字号 评论关闭

Windows网络编程杂谈
关于bind:
  INADDR_ANY 的具体含义是,绑定到0.0.0.0。此时,对所有的地址都将是有效的,如果系统考虑冗余,采用多个网卡的话,那么使用此种bind,将在所有网卡上进行绑定。在这种情况下,你可以收到发送到所有有效地址上数据包。
  例如:
  SOCKADDR_IN Local;
  Local.sin_addr.s_addr = htonl(INADDR_ANY);
  
  另外一种方式如下:
  SOCKADDR_IN Local;
  hostent* thisHost = gethostbyname("");
  char* ip = inet_ntoa(*(struct in_addr *)*thisHost->h_addr_list);
  Local.sin_addr.s_addr = inet_addr(ip);
 
  在这种方式下,将在系统中当前第一个可用地址上进行绑定。在多网卡的环境下,可能会出问题。 
  最常见的方式:
  const char LocalIP[] = "192.168.0.100";
  SOCKADDR_IN Local;
  Local.sin_addr.s_addr = inet_addr(LocalIP);
  bind(socket, (LPSOCKADDR)&Local, sizeof(SOCKADDR_IN)

bind的安全问题:
如果你在bind时,使用了INADDR_ANY那么,你将可以在所有有效的地址上进行监听,但是Socket有一个特性:可在同一端口上绑定多个Socket。
让我们看看下面的情况:假设你的系统只有一个IP:192.168.0.100,你希望bind到4096端口。对于下面的两种bind,当数据包到达时,谁会接收到呢?
 
  Local.sin_addr.s_addr = htonl(INADDR_ANY);
  Local.sin_addr.s_addr = inet_addr("192.168.0.100");

  WinSocke库是这样处理的:谁绑定的最明确,谁获取数据包。显然,第二种bind将获取到达的数据包。如果避免这种情况呢?使用SO_EXECLUSINEADDRUSE选项。需要注意的是,此选项在Windows NT 4 Service Pack 4以后(包括SP4)才可以使用。
  
示例代码:
  #ifndef SO_EXECLUSINEADDRUSE
  #define SO_EXECLUSINEADDRUSE ((int)(~SO_REUSEADDR))
  #endif

  SOCKADDR_IN Local;
  BOOL val = TRUE;
  Local. sin_family = AF_INET;
  Local. sin_port = htons(4096);
  Local.sin_addr.s_addr = htonl(INADDR_ANY);
  
  setsocketopt(socket, SOL_SOCKET, SO_EXECLUSINEADDRUSE,
(char*)&val,sizeof(val));
  bind(socket, (LPSOCKADDR)&Local, sizeof(SOCKADDR_IN)
 
1. WinSokcet Architecture
 
  2. 关于AcceptEx
使用此函数时,要包含头文:Mswsock.h,同时要链接:Mswsock.lib。可在源程序中加入下面的语句,这样在编译时,将自动链接Mswsock.lib。
 
  #pragma comment(lib," Mswsock.lib")
  
  下面是使用AcceptEx函数的示例代码:
  #define STRICT
  #define _WIN32_WINNT 0x0500 // Windows 2000 or later
  #define WIN32_LEAN_AND_MEAN
  #pragma comment(lib,"Ws2_32.lib")
  #pragma comment(lib,"Mswsock.lib")
  int main()
  {
      const int BUFSIZE = 48;
   LPFN_ACCEPTEX lpfnAcceptEx = NULL;
      GUID GuidAcceptEx = WSAID_ACCEPTEX;
      DWORD dwBytes = 0;
      SOCKET ListenSocket = INVALID_SOCKET;
      SOCKET ClientSocket = INVALID_SOCKET;
      HANDLE hCompPort = INVALID_HANDLE_VALUE;
      OVERLAPPED ol;
      char buf[BUFSIZE];
  
      // Init WinSock Lib ....
  
       ListenSocket = WSASocket(AF_IPX, SOCK_STREAM, NSPROTO_SPX, NULL, 0, WSA_FLAG_OVERLAPPED);
 
       ClientSocket = WSASocket(AF_IPX, SOCK_STREAM, NSPROTO_SPX, NULL, 0, WSA_FLAG_OVERLAPPED);
  
       // Bind && Listen ....
  
       // Associate the listening socket with the completion port
       CreateIoCompletionPort((HANDLE)ListenSocket, hCompPort, (u_long)0, 0);
 
// Get AccpetEx Function
       WSAIoctl(ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx, sizeof(GuidAcceptEx),
       GUID GuidAcceptEx = WSAID_ACCEPTEX;
       DWORD dwBytes = 0;
       SOCKET ListenSocket = INVALID_SOCKET;
       SOCKET ClientSocket = INVALID_SOCKET;
       HANDLE hCompPort = INVALID_HANDLE_VALUE;
       OVERLAPPED ol;
       char buf[BUFSIZE];
  
      // Init WinSock Lib .... 
      ListenSocket = WSASocket(AF_IPX, SOCK_STREAM, NSPROTO_SPX, NULL, 0, WSA_FLAG_OVERLAPPED);
      ClientSocket = WSASocket(AF_IPX, SOCK_STREAM, NSPROTO_SPX, NULL, 0, WSA_FLAG_OVERLAPPED);
 
     // Bind && Listen ....
  
     // Associate the listening socket with the completion port
     CreateIoCompletionPort((HANDLE)ListenSocket, hCompPort, (u_long)0, 0);
 
// Get AccpetEx Function
WSAIoctl(ListenSocket,SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,sizeof(GuidAcceptEx),&lpfnAcceptEx,
sizeof(m_WorkInfo.AcceptInfo.lpfnAcceptEx), &dwBytes, NULL, NULL );
ZeroMemory(buf,BUFSIZE);
   ZeroMemory(&ol,sizeof(OVERLAPPED));
   // Post Accept Message
   lpfnAcceptEx(ListenSocket, ClientSocket, buf, 0,
sizeof(SOCKADDR_IN)+ 16,
sizeof(SOCKADDR_IN) + 16, &dwBytes, &ol );
}
 
  需要注意的是,通过WSAIoctl获取AcceptEx函数指针时,只需要传递给WSAIoctl一个有效的SOCKET即可,该Socket的类型不会影响获取的AcceptEx函数指针。
  如果不希望AcceptEx建立连接后等待用户发送数据,那么必须将第四个参数设为0。第5、6参数必须是对应SOCKET的地址类型的大小再加上16个字节。
  为了使服务器能较好的处理用户连接请求,可采取如下两种策略:
  A.设定两个界限值,使系统未处理的Accept操作保持在一个固定水平。推荐上限为10;
  B.通过WSAEventSelect函数监听ListenSocket上的FD_ACCEPT事件。
  当关闭完成端口时,如果还有未处理的Accepte操作,应该先关闭ListenSocket,然后在IOCP中,处理这些Accept操作(进行资源释放等),切记不要强行终止那些没有处理的Accept操作,否则会造成内存泄漏。
  为防止恶意用户(建立连接后,不发送数据),可设置ListenSocket的SO_CONNECT_TIME属性。
  如果希望ClientSocket具有和ListenSocket相同的属性,需要对ClientSocket调用SO_UPDATE_ACCEPT_CONTEXT。
 

抱歉!评论已关闭.