Socket那些事——winsocket日记(1)

来源:互联网 发布:知乎如何匿名提问 编辑:程序博客网 时间:2024/05/18 16:38

般来说 碰到的第一个结构体是 WSADATA

 

 

[cpp] view plaincopy
  1. #define WSADESCRIPTION_LEN      256  
  2. #define WSASYS_STATUS_LEN       128  
  3. typedef struct WSAData {  
  4.         WORD                    wVersion;  
  5.         WORD                    wHighVersion;  
  6. #ifdef _WIN64  
  7.         unsigned short          iMaxSockets;  
  8.         unsigned short          iMaxUdpDg;  
  9.         char FAR *              lpVendorInfo;  
  10.         char                    szDescription[WSADESCRIPTION_LEN+1];  
  11.         char                    szSystemStatus[WSASYS_STATUS_LEN+1];  
  12. #else  
  13.         char                    szDescription[WSADESCRIPTION_LEN+1];  
  14.         char                    szSystemStatus[WSASYS_STATUS_LEN+1];  
  15.         unsigned short          iMaxSockets;  
  16.         unsigned short          iMaxUdpDg;  
  17.         char FAR *              lpVendorInfo;  
  18. #endif  
  19. } WSADATA  

 

wVersion 是Socket现存版本号

wHighVersion 是支持的最高版本号

szDescription 以字符形式存储socket信息

szSystemStatus 以字符形式存储客户需求信息

iMaxSockets 单个进程所能创建的socket最大数

lpVendorInfo 一般超出了winsock的范围

 

结构体 SYSTEM_INFO

 

[cpp] view plaincopy
  1. typedef struct _SYSTEM_INFO {  
  2.     union {  
  3.         DWORD dwOemId;          // Obsolete field...do not use  
  4.         struct {  
  5.             WORD wProcessorArchitecture;  
  6.             WORD wReserved;  
  7.         };  
  8.     };  
  9.     DWORD dwPageSize;  
  10.     LPVOID lpMinimumApplicationAddress;  
  11.     LPVOID lpMaximumApplicationAddress;  
  12.     DWORD_PTR dwActiveProcessorMask;  
  13.     DWORD dwNumberOfProcessors;  
  14.     DWORD dwProcessorType;  
  15.     DWORD dwAllocationGranularity;  
  16.     WORD wProcessorLevel;  
  17.     WORD wProcessorRevision;  
  18. } SYSTEM_INFO  

在这里我们只用到dwNumberOfProcessors其表示为系统可识别的cpu数

使用系统函数GetSystemInfo()得到系统信息

 

WSAStartup(
    IN WORD wVersionRequested,
    OUT LPWSADATA lpWSAData
    );函数登场了

从参数声明就可以看出其中wVersionRequested是一个输入字段WORD 长为双八位,可以使用MAKEWORD(2,2)或者0x202来传入参数,当主版本号和副版本号不一定都为2但是对于现在的win2003来说可以肯定为2,2.

lpWSAData 为传出参数 ,需要传入一个声明过的参数指针,以便他初始化,当然他的结构是WSADATA

 

因为我的网络通讯程序是建立在高并发的基础上的所以CreateIoCompletionPort这个函数就不得不说了

这个函数具有两种作用:

1、创建完成端口

2、进行socket和完成端口的绑定

 

结构体addrinfo

[c-sharp] view plaincopy
  1. typedef struct addrinfo  
  2. {  
  3.     int                 ai_flags;       // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST  
  4.     int                 ai_family;      // PF_xxx  
  5.     int                 ai_socktype;    // SOCK_xxx  
  6.     int                 ai_protocol;    // 0 or IPPROTO_xxx for IPv4 and IPv6  
  7.     size_t              ai_addrlen;     // Length of ai_addr  
  8.     char *              ai_canonname;   // Canonical name for nodename  
  9.     __field_bcount(ai_addrlen) struct sockaddr *   ai_addr;        // Binary address  
  10.     struct addrinfo *   ai_next;        // Next structure in linked list  
  11. }  
  12. ADDRINFOA, *PADDRINFOA;  

ai_flags 

 AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。
当节点名位NULL,且此标志置位,则返回的地址将是通配地址。
如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。
AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。
AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。

ai_family 

AF_INET    IPv4
AF_INET6   IPv6
AF_UNSPEC  协议无关

 

ai_socktype

SOCK_STREAM   流        TCP
SOCK_DGRAM   数据报   UDP 

 

ai_protocol    

IPPROTO_IP    0  IP协议
IPPROTO_IPV4   4  IPv4
IPPROTO_IPV6   41 IPv6
IPPROTO_UDP   17 UDP
IPPROTO_TCP   6  TCP

ai_addrlen    记录 ai_addr 信息的长度 一般为16字节

 

ai_canonname  节点名

 

ai_addr    记录sockaddr结构体的指针

 

ai_next   下一个addrinfo结构体指针

 

下面就是getaddrinfo函数

WINSOCK_API_LINKAGE
INT
WSAAPI
getaddrinfo(
    __in_opt        PCSTR               pNodeName,
    __in_opt        PCSTR               pServiceName,
    __in_opt        const ADDRINFOA *   pHints,
    __deref_out     PADDRINFOA *        ppResult
    );
它的作用就是通过设置ip端口号、传入一个设置了一个了一部分数据的addrinfo结构体指针,返回一个addrinfo结构体

 

函数WSASocket用于创建一个与指定传送服务提供者捆绑的套接口

WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED)

AF_INET IPV4地址结构,

SOCK_STREAM TCP,

IPPROTO_IP IP协议,

定义所创建套接口的特性  如果非零前三项被忽略

套接口组的描述字,

套接口属性描述  异步重叠方式

 

setsockopt() 函数

int PASCAL FAR setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen);

s 标识一个套接口的描述字。

level 选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。

optname 需设置的选项。

optval 指针,指向存放选项值的缓冲区。

optlen optval缓冲区的长度。

 

1.设置调用closesocket()后,仍可继续重用该socket。调用closesocket()一般不会立即关闭socket,而经历TIME_WAIT的过程。

BOOL bReuseaddr = TRUE;

setsockopt( s, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( BOOL ) );

2. 如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:

BOOL bDontLinger = FALSE;

setsockopt( s, SOL_SOCKET, SO_DONTLINGER, ( const char* )&bDontLinger, sizeof( BOOL ) );

3.在send(),recv()过程中有时由于网络状况等原因,收发不能预期进行,可以设置收发时限:

int nNetTimeout = 1000; //1秒

//发送时限

setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof( int ) );

//接收时限

setsockopt( socket, SOL_S0CKET, SO_RCVTIMEO, ( char * )&nNetTimeout, sizeof( int ) );

4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中如果发送或是接收的数据量比较大,可以设置socket缓冲区,避免send(),recv()不断的循环收发:

// 接收缓冲区

int nRecvBuf = 32 * 1024; //设置为32K

setsockopt( s, SOL_SOCKET, SO_RCVBUF, ( const char* )&nRecvBuf, sizeof( int ) );

//发送缓冲区

int nSendBuf = 32*1024; //设置为32K

setsockopt( s, SOL_SOCKET, SO_SNDBUF, ( const char* )&nSendBuf, sizeof( int ) );

5.在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:

int nZero = 0;

setsockopt( socket, SOL_S0CKET, SO_SNDBUF, ( char * )&nZero, sizeof( nZero ) );

6.在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:

int nZero = 0;

setsockopt( s, SOL_SOCKET, SO_RCVBUF, ( char * )&nZero, sizeof( int ) );

7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

BOOL bBroadcast = TRUE;

setsockopt( s, SOL_SOCKET, SO_BROADCAST, ( const char* )&bBroadcast, sizeof( BOOL ) );

8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)

BOOL bConditionalAccept = TRUE;

setsockopt( s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, ( const char* )&bConditionalAccept, sizeof( BOOL ) );

9.如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了closesocket(),以前一般采取的措施是shutdown(s,SD_BOTH),但是数据将会丢失。某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:

struct linger {

     u_short l_onoff;

  u_short l_linger;

};

linger m_sLinger;

m_sLinger.l_onoff = 1; //在调用closesocket()时还有数据未发送完,允许等待

// 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭

m_sLinger.l_linger = 5; //设置等待时间为5秒

setsockopt( s, SOL_SOCKET, SO_LINGER, ( const char* )&m_sLinger, sizeof( linger ) );

 

SO_BROADCAST BOOL 允许套接口传送广播信息。

SO_DEBUG BOOL 记录调试信息。

SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。

SO_DONTROUTE BOOL 禁止选径;直接传送。

SO_KEEPALIVE BOOL 发送“保持活动”包。

SO_LINGER struct linger FAR* 如关闭时有未发送数据,则逗留。

SO_OOBINLINE BOOL 在常规数据流中接收带外数据。

SO_RCVBUF int 为接收确定缓冲区大小。

SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑(参见bind())。

SO_SNDBUF int 指定发送缓冲区大小。

TCP_NODELAY BOOL 禁止发送合并的Nagle算法。

 

TCP_NODELAY是唯一使用IPPROTO_TCP层的选项,其他所有选项都使用SOL_SOCKET层。


bind()

只是把IP地址绑定到socket上

 

listen()函数是设置socket在接入时系统允许等待的最大用户连接数,在winsock中系统默认最大为200、最小5,所以超出界限的设置是没有意义的。


原创粉丝点击