分配给套接字的IP地址和端口
IP:Internet Protocol(网络协议),为了收发数据而分配给计算机的值
IP地址也叫网络地址,分为两类:IPv4(4字节)和IPv6(16字节)
IPv4地址分为A、B、C、D类,前面固定的是网络地址,后面可变的是主机地址,数据会先传到网络里,网络(交换机、路由器)接收到数据会根据主机地址传给指定计算机
IPv4分类 | 首位 | 首字节范围 | 网络地址字节数 | 主机地址字节数 |
---|---|---|---|---|
A类地址 | 0 | 0-127 | 1 | 3 |
B类地址 | 10 | 128-191 | 2 | 2 |
C类地址 | 110 | 192-223 | 3 | 1 |
端口号是为了区分程序中创建的套接字而分配给套接字的序号
端口号用于区分应用程序,是在同一操作系统内为区分不同套接字而设置的,一个端口号只能有一个套接字,TCP套接字和UDP套接字不会共用端口号
端口号由16位组成,范围是0-65535,其中0-1023是分配给特定程序的
地址信息的表示
IP地址和端口号在应用程序中以结构体的形式给出定义
Linux 套接字地址结构体:
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); /*协议族 sin_family Address Family */
in_port_t sin_port; /*16位端口号 Port number. */
struct in_addr sin_addr; /*32位IP地址 Internet address. */
// 下面这个不使用
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr)
- __SOCKADDR_COMMON_SIZE
- sizeof (in_port_t)
- sizeof (struct in_addr)];
};
- sin_family:保存协议族,常见有AF_INET,AF_INET6,AF_LOCAL(UNIX协议地址族)
- sin_port:端口号,以网络字节序保存
- sin_addr:IP地址,以网络字节序保存,当作32位整数就行
- sin_zero:无特殊含义,为了让sockaddr_in和sockaddr结构体大小相同而插入的成员
绑定地址的时候使用bind函数:
sockaddr_in serv_addr;
...
if(bind(serv_sock,(const sockaddr *)&serv_addr,sizeof(serv_addr)) == -1){
这里的第二个参数需要的是sockaddr的值,sockaddr结构体如下:
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
因为直接向sockaddr填入有些麻烦,所以通过sockaddr_in来进行转换
网络字节序与地址转换
数据在内存中有两种保存/解析方式:
- 大端序:高位字节存放到低位
- 小端序:高位字节存放到高位
数据 | 内存大端序 | 内存小端序 |
---|---|---|
0x12345678 | 0x12 0x34 0x56 0x78 | 0x78 0x56 0x34 0x12 |
Intel和amd系列CPU以小端序方式保存数据
因为有两种解析方式,所以网络传输约定统一使用网络字节序--大端序,小端序系统传输数据时应将数据转化位大端序排序方式
字节序转换函数:htons,ntohs,htonl,ntohl,函数名是h,n,to,l,s字符的组合:
- h表示主机字节序
- n表示网络字节序
- s指的是short类型(2字节,用于端口号转换)
- l指的是long类型(4字节,用于IP地址转换)
例如:htons:把short类型数据从主机字节序转化为网络字节序
数据收发过程中两种字节序会自动进行转换,只有填充sockaddr_in结构体的时候需要手动转换一下
网络地址的初始化和分配
将字符串信息转换成网络字节序的整数型,sockaddr_in里保存地址信息的成员是32位整数型,有个函数可以帮助进行转换:
in_addr_t inet_addr (const char *__cp);
int inet_aton (const char *__cp, struct in_addr *__inp); //成功返回1 失败返回0
char *inet_ntoa (struct in_addr __in);
其中inet_addr仅仅是转换字节序,inet_aton功能相同,不过可以直接把返回值返回到in_addr结构体里(参数里返回),inet_ntoa和inet_aton功能相反,将网络字节序地址转换成字符串形式
套接字常见的网络地址初始化方法:
struct sockaddr_in addr = {0};
char* serv_ip = "123.112.34.1";
char* serv_port = "1080";
addr.sin_family = AF_INET; // 指定IPv4
addr.sin_addr.s_addr = inet_addr(serv_ip); // 基于字符串的IP地址初始化,也可以使用INADDR_ANY表示全部IP地址
addr.sin_port = htons(aoti(serv_port)); // 基于字符串的port初始化
基于Windows的实现
Windows的htons和htonl的用法与Linux一样
Windows中转换IP字节序只有inet_addr和inet_ntoa,用法与Linux一样
向套接字分配网络地址的过程和Linux相同,bind函数用法是一样的
在Winsock2中新增了两个转换函数:WSAStringToAddress和WSAAddressToString
功能上与inet_ntoa和inet_addr完全相同,但支持多种协议,IPv4和IPv6都适用
使用和Linux相同的一组函数处理网络地址更方便在Windows和Linux上移植