Windows Socket是以Unix Socket为基础发展而来,所以很多函数与Unix/Linux下的API相同。
套接字是一条连接的两端,每个端点由IP和端口号组成,IP标示了网络中的主机,端口号标示了IP对应的主机的某个进程,因此一个链接表示的是一个进程与一个进程间的通信。所以Socket通信时进程间通信的一种方式。
TCP & UDP
TCP,Transmission Control Protocol,传输控制协议,面向连接、可靠的传输层协议,用于数据量小但安全性高的场合。
UDP,User Datagram Protocol,用户数据报协议,面向无连接、不可靠的传输层协议,用户数据量大但误差要求不高的场合。
面向连接的套接字通信时序图如下:
Windows环境下,调用socket API需要包含windows socket动态库
#include <winsock2.h> //<winsock.h>
#pragma comment(lib,"WS2_32.lib")
并且在调用socket API之前得首先调用WSAStartup()
以加载动态库。
/*
* wVersionRequested:动态库的版本,可使用MAKEWORD宏组成
* lpwsadata:返回加载的动态库的基本信息,如加载是否成功,实际版本等。
*/
int WSAStartup(WORD wVersionRequested,LPWSADATA lpwsadata);
关键类型及结构体
1. SOCKET
SOCKET被定义为一个套接字的标识符,实际上就是一个无符整形数,用以表示不同的连接。
2. sockaddr_in
struct sockaddr_in
结构体用于描述一个端口的地址信息。定义如下:
/*
* sin_family:使用的协议簇,AF_INET表示IP协议
* sin_port:端口号,用户指定,需转换为网络字序
* sin_addr:IP地址,结构体用以表示四字节IP地址
* sin_zero:零字节填充,用以兼容struct sockaddr
*/
struct sockaddr_in
{
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
3. in_addr
/*
* S_un_b:用四个unsigned char表示IP地址
* S_un_w:用两个unsigned short 表示IP地址
* S_addr:用一个usnigned long表示IP地址
*/
struct in_addr
{
union
{
struct
{
UCHAR s_b1,s_b2,s_b3,s_b4;
}S_un_b;
struct
{
USHORT s_w1,s_w2;
}S_un_w;
ULONG S_addr;
} S_un;
}
字节序问题
数据存储存在大小端问题,且不同CPU存储数据格式不同,而数据传输也存在大小端问题,对于一个四字节的数据0x12345678,大小端内存对应如下:
内存地址 | 0x0001 | 0x0002 | 0x0003 | 0x0004 |
---|---|---|---|---|
大端 | 12 | 34 | 56 | 78 |
小端 | 78 | 56 | 34 | 12 |
可以看出:big-endian最直观,little-endian最符合人的思维。
为了保证网络传输的统一性,TCP/IP协议规定网络数据传输一律采用Big-endian格式,与CPU和操作系统无关。为了实现CPU字节序到网络字节序的转换,BSD socket提供了以下函数:
/* host to net(short),本机字节序到网络字节序转换(short) */
uint16_t htons(uint16_t hostshort);
/* host to net(long),本机字节序到网络字节序转换(long) */
uint32_t htonl(uint3