端口号由16位构成,可分配的端口号范围是0-65535。但0-1023是知名端口,一般分配给特定应用程序。虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口,所以允许重复。例如:如果某TCP套接字使用9190号端口,则其他TCP套接字就无法使用该端口号,但UDP套接字可以使用。
地址信息的表示
应用程序中使用的IP地址和端口号以结构体的形式给出了定义,结构体作为地址信息传递给bind函数。
struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family)
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
};
该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址
struct in_addr{
In_addr_t s_addr; //32位IPv4
};
uint16_t、in_addr_t等类型可以参考POSIX(Portable Operating System Interface,可移植操作系统接口)。POSIX是为UNIX系列操作系统设立的标准
成员sin_family
成员sin_port
以网络字节序保存16位端口号
成员sin_addr
以网络字节序保存32位IP地址信息
成员sin_zero
无特殊含义,只是为使结构体sockaddr_in的大小与sockaddr结构体保持一致而插入的成员,必须填充为0。
struct sockaddr_in serv_addr;
//...
if(bind(serv_sock,(struct sockaddr *) &serv_addr,sizeof(serv_addr))==-1)
//...
直接向sockaddr结构体填充信息会带来麻烦
struct sockaddr{
sa_family_t sin_family; //地址族
char sa_data[14]; //地址信息
};
结构体sockaddr并非只为IPV4设计,这从保存地址信息的数组sa_data长度为14字节可看出。因此,结构体sockaddr要求在sin_family中指定地址族信息。为了与sockaddr保持一致,sockaddr_in结构体中也有地址族信息。
网络字节序与地址变换
大端序:高位字节存放到低位地址
小端序:高位字节存放到高位地址
通过网络传输数据时约定统一方式,这种约定称为网络字节序——统一为大端序。
字节序转换
在填充sockadr_in结构体前将数据转换成网络字节序。转换字节序的函数如下:
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
htons中的h代表主机(host)字节序,n代表网络字节序。
除了向sockaddr_in结构体填充数据外,其他情况无需考虑字节序问题。(自动转换)
网络地址的初始化与分配
inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。
inet_ntoa函数将网络字节序整数型IP地址转换成我们熟悉的字符串形式
网络地址初始化
指定ip地址的初始化:
struct sockaddr_in addr;
char* serv_ip = "211.217.168.13";//声明IP地址字符串
char* serv_port="9190"; //声明端口号字符串
memset(&addr,0,sizeof(addr)); //结构体变量addr的所有成员初始化为0
addr.sin_family = AF_INET; // 指定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip); //基于字符串IP地址初始化
addr.sin_port = htons(atoi(serv_port)); //基于字符串的端口初始化
//int atoi(const char *str) 把参数 str 所指向的字符串转换为int型
struct sockaddr_in addr;
char* serv_port="9190"; //声明端口号字符串
memset(&addr,0,sizeof(addr)); //结构体变量addr的所有成员初始化为0
addr.sin_family = AF_INET; // 指定地址族
addr.sin_addr.s_addr = inet_addr(INADDR_ANY); //基于字符串IP地址初始化
addr.sin_port = htons(atoi(serv_port)); //基于字符串的端口初始化
使用常数INADDR_ANY分配服务器端的IP地址,可以自动获取运行服务器端的计算机IP地址,而且,若同一计算机中已分配多个IP地址,则只要端口号一致,就可以从不同IP地址接收数据。
向套接字分配网络地址
当函数调用成功时,将第二个参数指定的地址信息分配给第一个参数中的相应套接字。