windows Socket

Windows socket编程基础

一、Winsock初始化

在进行winsock编程前需要进行初始化,调用WSAStartup函数,设置当前的winsock的版本和初始化相对应版本的库。

1
2
#include <winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA, lpWSAData);
  • wVersionRequested:当前所使用的winsock版本信息。使用MAKEWORD()函数,产生一个版本号,副版本号为高位,主版本号为低位,如版本号为2.1,则MAKEWORD(1,2),返回0x0102。
  • lpWSAData:WSAData结构体变量指针
  • return:0成功,非0失败

当要注销该库时使用函数WSACleanup(void);,成功返回0,失败返回SOCKET_ERROR。

二、数据类型之间的转换

网络与本低字节序的转换

在计算机当中,根据cpu或操作系统的不同可分为大端存储(Big Endian)和小端存储(Little Endian)。

大端存储:高位字节存放到地位地址
小端存储:高位字节存放到高位地址

字节序转换函数

1
2
3
4
unsigned short htos(unsigned short);
unsigned short ntoh(unsigned short);
unsigned long htol(unsigned long);
unsigned long ntoh(unsigned long);

h代表主机(host)字节序,n代表网络(network)字节序。

在windows平台下,大部分采用大端法,但是在网络中以小端法方式传输,所以在接收时要进行字节序的转换。

字符串跟IP地址之间的转换

从字符串转换到IP

在SOCKADDR_IN的sin_addr中保存的是32位整数型,而人为添加的IP是一个点分十进制字符串,所以需要通过转换才能使IP地址被传递到SOCKADDR_IN的变量中。这就是所谓的网络字节序的转换。

1
2
3
//windows platform
#include <WinSock2.h>
unsigned long inet_addr(const char *string);
  • return:成功时返回32位小端(或大端)序整数,失败返回INADDR_NONE

从IP转换到字符串

有时候需要获取到从网络当中获得的IP地址并通过点分十进制显示出来。在Windows平台上只有inet_ntoa(),没有inet_aton()。

1
2
3
//windows platform
#include <WinSock2.h>
char* inet_ntoa(struct in_addr adr);
  • in:具有整型IP地址的in_addr变量。
  • return:成功返回地址字符串,失败返回-1。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*inet_addr用例*/
{
char *ip_str1 = "192.168.0.1";
SOCKADDR_IN sockaddr;
//将字符串转换成整数
sockaddr.sin_addr.s_addr = inet_addr(ip_str1);
}

/*inet_ntoa用例*/
{
SOCKADDR_IN sockaddr;
char ip_str[20];
sockaddr.sin_addr.s_addr = htonl(0x02030104);

ip_str = inet_ntoa(sockaddr.sin_addr);
}

WinSock2自带转换函数

WSAStringToAddress&WSAAddressToString,这两个转换函数功能跟inet_ntoa,inet_addr一致,支持IPv4和IPv6,但是不支持跨平台,兼容性差。

WSAStringToAddress

1
2
3
4
5
6
7
8
#include <WinSock2.h>
INT WSAStringToAddress(
_In_ LPWSTR AddressString,
_In_ INT AddressFamily,
_In_opt_ LPWSAPROTOCOL_INFOW lpProtocolInfo,
_Out_writes_bytes_to_(*lpAddressLength,*lpAddressLength) LPSOCKADDR lpAddress,
_Inout_ LPINT lpAddressLength
)
  • AddressString:含有端口号和ip的字符串。
  • AddressFamily:地址协议族。
  • lpProtocolInfo:设置协议提供者,默认为NULL。
  • lpAddress:保存地址信息的SOCKADDR变量。
  • lpAddressLength:lpAddress长度的指针。
  • return:成功返回0,失败返回SOCKET_ERROR。

WSAAddressToString

1
2
3
4
5
6
7
INT WSAAddressToString(
_In_reads_bytes_(dwAddressLength) LPSOCKADDR lpsaAddress,
_In_ DWORD dwAddressLength,
_In_opt_ LPWSAPROTOCOL_INFOW lpProtocolInfo,
_Out_writes_to_(*lpdwAddressStringLength,*lpdwAddressStringLength) LPWSTR lpszAddressString,
_Inout_ LPDWORD lpdwAddressStringLength
)
  • lpsaAddress:需要转换的地址信息的SOCKADDR变量。
  • dwAddressLength:lpsaAddress长度的指针。
  • lpProtocolInfo:设置协议提供者,默认为NULL。
  • lpszAddressString:保存转换结果的字符串。
  • lpdwAddressStringLength:lpszAddressString存有地址信息的长度。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*WSAStringToAddress用例*/
{
char *ip_str = "192.168.0.1:9090";
SOCKADDR_IN sockaddr;
int socklen = sizeof(sockaddr);
//将字符串IP转换成socket地址
WSAStringToAddress(ip_str, AF_INET, NULL, (SOCKADDR*)&sockaddr, &socklen);
}

/*WSAAddressToString用例*/
{
char ip_str[30];
//sockaddr具有IP地址和端口号信息
SOCKADDR_IN sockaddr;
int socklen = sizeof(sockaddr);
int ip_buf_len = sizeof(ip_str);
WSAAddressToString((SOCKADDR*)&sockaddr, &socklen, NULL, ip_str, &ip_buf_len);
}

三、Windows socket相关函数

连接相关的函数都在WinSock2.h中.

1
SOCKET socket(int af, int type, int protocol);

  • af:协议族,IPv4(AF_INET)或IPv6
  • type:socket类型,SOCK_STREAM, SOCKET_DGRAM, SOCK_RAW
  • protocol:socket使用的特定协议,默认为0
  • return:成功返回套接字句柄,失败返回INVALID_SOCKET
1
int bind(SOCKET s, const struct sockaddr * name, int namelen);
  • s:socket返回的套接字
  • name:SOCKADDR类型的指针,可以通过强制类型转换,将SOCKADDR_IN转成SOCKADDR。
  • namelen:name的长度。
  • return:成功返回0,失败返回SOCKET_ERROR
1
int listen(SOCKET s, int backlog);
  • s:socket返回的套接字
  • backlog:请求连接队列的最大长度。
1
int accept(SOCKET s, struct sockaddr *addr, int *addrlen);
  • s:socket返回的套接字
  • addr:SOCKADDR类型的指针,可以通过强制类型转换,将SOCKADDR_IN转成SOCKADDR。
  • addrlen:addr的长度。
  • return:成功返回指向客户端的套接字,失败返回INVALID_SOCKET
1
int connect(SOCKET s, const struct sockaddr *name, int namelen);
  • s:socket返回的套接字
  • name:SOCKADDR类型的指针,可以通过强制类型转换,将SOCKADDR_IN转成SOCKADDR。
  • namelen:name的长度。
  • return:成功返回0,失败返回SOCKET_ERROR
1
int closesocket(SOCKET s);
  • s:要断开的socket套接字
  • return:成功返回0,失败返回SOCKET_ERROR。

    1
    int shutdonw(SOCKET s, int how);
  • s:要断开的socket套接字

  • how:断开方式。SD_RECEIVE:断开输入流;SD_SEND:断开输出流;SD_BOTH:同时断开I/O流。
  • return:成功返回0,失败返回SOCKET_ERROR。

TCP数据传输I/O函数


1
int send(SOCKET s, const char *buf, int len, int flags);

  • s:socket返回的套接字
  • buf:发送的数据内容
  • len:发送数据的最大长度
  • flags:标识,默认0即可
  • return:发送成功返回发送个数
1
int recv(SOCKET s, const char *buf, int len, int flags);
  • s:socket返回的套接字
  • buf:接收数据存放的数组
  • len:接收数据的最大长度
  • flags:标识,默认0即可
  • return:接收成功返回实际接收个数

UDP数据传输I/O函数

UDP连接不需要确认就可以直接发送数据

1
2
#include <WinSock2.h>
int sendto(SOCKET s, const char * buf, int len, int flags, const struct sockaddr * to, int tolen);

  • s:socket返回的套接字
  • buf:待发送数据内容
  • len:待传输的数据长度,以字节为单位
  • flags:标识,默认0即可
  • to:存有目标地址的SOCKADDR结构体变量地址值
  • tolen:to地址值结构体变量长度
  • return:成功返回传输字节数,失败返回-1
1
int recvfrom(SOCKET s, const char * buf, int len, int flags, const struct sockaddr * from, int *fromlen);
  • s:socket返回的套接字
  • buf:待接收数据的数组
  • len:可接受的数据的最大长度
  • flags:标识,默认0即可
  • from:存有发送端地址的SOCKADDR结构体变量地址值
  • fromlen:from地址值结构体变量指针长度
  • return:成功返回传输字节数,失败返回-1
0%