3.1 分配给套接字的IP地址与端口号
IP是InternetProtocol(网络协议)的简写,是为收发网络数据而分配给计算机的值。
端口号并非赋予计算机的值,而是为区分程序中创建的套接字而分配给套接字的序号。
1. 网络地址(Internet Address)
- IPv4 4字节地址族
- IPv6 16字节地址族
IPv4标准的4字节IP地址分为网络地址和主机(指计算机)地址,且分为A、B、C、D、E等类型。
关注:路由器与交换机的作用。
2. 用于区分套接字的端口号
计算机中一般配有NIC(NetworkInterface Card,网络接口卡)数据传输设备。
通过NIC向计算机内部传输数据时会用到IP。操作系统负责把传递到内部的数据适当分配给套接字,这时就要利用端口号。
端口号就是在同一操作系统内为区分不同套接字而设置的,因此无法将1个端口号分配给不同套接字。
另外,端口号由16位构成,可分配的端口号范围是0~65535。但0~1023是知名端口(Well-Known PORT),一般分配给特定应用程序。
另外,虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许跨协议的端口号重复。
总之,数据传输目标地址同时包含IP地址和端口号,只有这样,数据才会被传输到最终的目的应用程序(应用程序套接字)。
3.2 地址信息的表示
应用程序中使用的IP地址和端口号以结构体的形式给出了定义。
1. 表示IPv4地址的结构体
struct sockaddr_in
{
sa_family_t sin_family; // 地址族
uint16_t sin_port; //16位TCP/IP端口号,网络字节序
struct in_addr sin_addr; // 32位IP地址,网络字节序
char sin_zero[8]; // 不使用,仅为使sockaddr_in与sockaddr结构体保持一致
};
struct in_addr
{
In_addr_t s_addr; // 32位IPv4地址
};
如果使用int32_t类型的数据,就能保证在任何时候都占用4字节,即使将来用64位表示int类型也是如此。
sockaddr_in 结构体变量地址值将以如下方式传递给bind函数:
struct sockaddr
{
sa_family_t sin_family; // 地址族
char sa_data[14]; // 地址信息
};
此结构体成员sa_data保存的地址信息中需包含IP地址号和端口号,剩余部分应填充0,这也是bind函数的要求。
sockaddr_in是保存IPv4地址信息的结构体,而sockaddr并非只为IPv4设计。所以仍需单独指定sin_family。
3.3 网络字节序与地址转换
代表CPU数据保存方式的主机字节序(HostByte Order)在不同CPU中也各不相同。在通过网络传输数据时,约定:统一使用网络字节序(NetworkByte Order),即大端字节序。
unsignedshort htons(unsigned short);
unsignedshort ntohs(unsigned short);
unsignedlong htonl(unsigned long);
unsignedlong ntohl(unsigned long);
htons中的h代表主机(host)字节序;n代表网络(network)字节序。
s指的是short,l指的是long。(Linux中long类型占用4个字节)
通常,以s作为后缀函数用于端口号转换,以l作为后缀函数用于IP地址转换。
问:数据传输过程中都需要转换吗?
答:除了向sockaddr_in结构体变量填充数据外,其他情况无需考虑字节序问题。
3.4 网络地址的初始化与分配
1. 将字符串信息转换为网络字节序的整数型
sockaddr_in中保存IP地址信息的成员为32位整数型,因此,为了分配IP地址,需要将点分十进制(DottedDecimal Notation)的IP地址化为32位整数型数据。
#include<arpa/inet.h>
in_addr_t inet_addr(const char *string);
//成功时返回32位大端序整数型值,失败时返回INADDR_NONE
该函数将字符串形式的IP地址转换成32位大端序(网络序)整数型IP地址。还可检测无效IP地址。
#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr *addr);
该函数与inet_addr函数功能相同,但其利用了in_addr结构体,使用频率很高;与inet_aton作用相反的函数:
#include<arpa/inet.h>
char* inet_ntoa(struct in_addr addr);
注意:调用时应将字符串信息复制到其他内存空间。(通过strcpy函数)
2. 常用的网络地址初始化方法
atoi函数把字符串转换成整型。
3. INADDR_ANY
每次创建服务器端套接字都要输入IP地址会有些繁琐,此时可如下初始化地址信息:
struct sockaddr_inaddr;
......
addr.sin_addr.s_addr= htonl(INADDR_ANY);
若采用这种方式,则可自动获取运行服务器端的计算机IP地址,不必亲自输入。而且,若同一计算机中已分配多个IP地址(多宿主(Multi-homed)计算机,一般路由器属于这一类),则只要端口号一致,就可以从不同IP地址接收数据。(实际IP地址的个数与计算机中安装的NIC的数量相等,若只有一个NIC,则直接使用INADDR_ANY)
127.0.0.1是回送地址(loopback address),指的是计算机自身IP地址。若用实际IP地址代替此地址也能正常运转。
4. 向套接字分配网络地址
#include<sys/socket.h>
int bind(int sockfd, structsockaddr *myaddr, socklen_t addrlen);
-sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符;
-myaddr: 存有地址信息的结构体变量地址值;
-addrlen: 第二个结构体变量的长度;