坂田网站建设公司,深圳 SEO 网站建设 哪里学,核心关键词如何优化,有服务器有域名如何做网站文章目录 分配给套接字的IP地址与端口号网络地址网络地址分类与主机地址边界 地址信息的表示表示 IPv4地址的结构体结构体sockaddr_in 的成员分析 网络字节序与地址变换字节序与网络字节序字节序转换 网络地址的初始化与分配将字符串信息转换为网络字节序的整数型网络地址初始化… 文章目录 分配给套接字的IP地址与端口号网络地址网络地址分类与主机地址边界 地址信息的表示表示 IPv4地址的结构体结构体sockaddr_in 的成员分析 网络字节序与地址变换字节序与网络字节序字节序转换 网络地址的初始化与分配将字符串信息转换为网络字节序的整数型网络地址初始化INADDR_ANY向套接字分配网络地址 总结 分配给套接字的IP地址与端口号
网络地址
IP地址分为两类
IPv4 4字节地址族IPv6 16字节地址族
IPv4和IPv6的差别主要是表示在IP地址所用的字节数目前通用的地址族为IPv4而IPv6是为了应对IP地址耗尽的问题而提出的标准目前主要还是使用IPv4
IPv4标准的4字节IP地址分为网络地址和主机地址且分为A、B、C、D等类型。
A类 网络ID 主机ID 主机ID 主机ID
B类 网络ID 网络ID 主机ID 主机ID
C类 网络ID 网络ID 网络ID 主机ID
D类 网络ID 网络ID 网络ID 网络ID 多播IP地址网络地址分类与主机地址边界
通过IP地址的第一个字节即可判断网络地址占用的字节数
A类地址的首字节范围0-127B类地址的首字节范围128-191C类地址的首字节范围191-223
还有另一种表述方式
A类地址的首位以0开始B类地址的首位以10开始C类地址的首位以110开始
地址信息的表示
表示 IPv4地址的结构体
struct sockaddr_in
{sa_family_t sin_family; //地址族uint16_t sin_port; //16位TCP/UDP端口号struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用
};该结构体中提到了另外一个结构体 in_addr 定义为
struct in_addr
{in_addr_t s_addr; //32位IPv4地址
}上述两个结构体包含一些数据类型uint16_t、in_addr_t等类型可以参考POSIX。POSIX是为UNIX系统操作设立的标准
POSIX中定义的数据类型
数据类型名称数据类型说明声明头文件int8_tsigned 8-bit intsys/types.huint8_tunsigned 8-bit intsys/types.hint16_tsigned 16-bit intsys/types.huint16_tunsigned 16-bit intsys/types.hint32_tsigned 32-bit intsys/types.huint32_tunsigned 32-bit intsys/types.hsa_family_t地址族(address family)sys/socket.hsocklen_t长度(length of struct)sys/socket.hin_addr_tIP地址,uint32_tnetinet/in.hin_port_t端口号uint16_tnetinet/in.h
结构体sockaddr_in 的成员分析
成员 sin_family
每种协议族适用的协议族均不同比如IPv4使用4字节地址族IPv6使用16字节地址族
地址族
地址族含义AF_INETIPv4网络协议中使用的地址族AF_INET6IPv6网络协议中使用的地址族AF_LOCAL本地通信中采用的UNIX协议的地址族
AF_LOCAL是为了说明具有多种地址族而添加的
成员sin_port
该成员保存16位端口号重点在于它以网络字节序保存
成员sin_addr
该成员保存32位IP地址信息也以网络字节序保存同时管擦结构体in_addr
成员sin_zero
无特殊含义只是为使结构体sockaddr_in的大小与sockaddr结构体保存一致而插入的成员必须填充为0
网络字节序与地址变换
字节序与网络字节序
CPU保存数据的方式有两种意味着CPU解析数据的方式也分为两种
大端序高位字节存放到低位地址小端序高位字节存放到高位地址
在通过网络传输数据时约定统一方式这种约定称为网络字节序统一为大端序
总结来说将数据数组转发成大端序格式再进行网络传输所有计算机接收数据时应该识别该数据是网络字节序格式小端序系统传输时应该转化成大端序排列方式
字节序转换
填充结构体 sockadr_in 前将数据转换成网络字节序介绍帮助转换字节序的函数
unsigned short htons(unsigned short)
unsigned short ntohs(unsigned short)
unsigned long htonl(unsigned short)
unsined long ntohl(unsigned long)函数名的含义
htons中的 h 代表主机(host)字节序htons中的 n 代表网络(network)字节序htons中的 s 指的是shorthtons中的 l 指的是long(Linux中long类型占用4个字节)
#include stdio.h
#include arpa/inet.hint main(int argc, char *argv[])
{unsigned short host_port0x1234;unsigned short net_port;unsigned long host_addr0x12345678;unsigned long net_addr;net_porthtons(host_port);net_addrhtonl(host_addr);printf(Host ordered port: %#x \n, host_port);printf(Network ordered port: %#x \n, net_port);printf(Host ordered address: %#lx \n, host_addr);printf(Network ordered address: %#lx \n, net_addr);return 0;
}下面这就是在小端序CPU中运行的结果。如果在大端序CPU中运行则变量值不会改变。大部分朋友都会得到类似的运行结果因为Intel和AMD系列的CPU都采用小端序标准。
gcc endian_conv.c -o conv
./conv
输出
Host ordered port : 0x1234
Network ordered port : 0x3412
Hostordered address : 0x12345678
Network ordered address : 0x78563412网络地址的初始化与分配
将字符串信息转换为网络字节序的整数型
对于IP地址的表示我们熟悉的是点分十进制表示法而非整数型数据表示法。幸运的是有函数会帮我们将字符串形式的IP地址转换成32位整数型数据在转换类型的同时进行网络字节序转换。
#includearpa/inet.h
in_addr_t inet_addr(const char * string);//成功时返回32位大端序整数型值失败时返回INADDR_NONE。该函数的调用过程
#include stdio.h
#include arpa/inet.hint main(int argc, char *argv[])
{char *addr1127.212.124.78;char *addr2127.212.124.256;unsigned long conv_addrinet_addr(addr1);if(conv_addrINADDR_NONE)printf(Error occured! \n);elseprintf(Network ordered integer addr: %#lx \n, conv_addr);conv_addrinet_addr(addr2);if(conv_addrINADDR_NONE)printf(Error occureded \n);elseprintf(Network ordered integer addr: %#lx \n\n, conv_addr);return 0;
}从结果可以看出inet_addr函数不仅可以把IP地址转成32位整数型而且可以检测无效的IP地址
inet_aton函数与inet_addr函数在功能上完全相同也将字符串形式IP地址转换为32位网络字节序整数并返回。不同的是该函数利用了in_addr结构体且使用频率更高。
#includearpa/inet.h
int inet_aton(const char * string, struct in_addr * addr);成功时返回1失败时返回0参数1string含有需转换的IP地址信息的字符串地址值。参数2addr将保存转换结果的in_addr结构体变量的地址值。该函数的调用过程
#include stdio.h
#include stdlib.h
#include arpa/inet.h
void error_handling(char *message);int main(int argc, char *argv[])
{char *addr127.232.124.79;struct sockaddr_in addr_inet;if(!inet_aton(addr, addr_inet.sin_addr))error_handling(Conversion error);elseprintf(Network ordered integer addr: %#x \n, addr_inet.sin_addr.s_addr);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}调用inet_addr函数返回转换后的IP地址信息还需保存到sockaddr_in结构体中声明的in_addr结构体变量。而inet_aton函数则不需此过程因为该函数会自动把结果存入该结构体变量。
还有一个函数与 inet_aton() 正好相反它可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式
#include arpa/inet.h
char *inet_ntoa(struct in_addr adr);成功返回转换的字符串地址值失败返回-1该函数将通过参数传入的整数型IP地址转换为字符串格式并返回 但要小心返回值为 char 指针返回字符串地址意味着字符串已经保存在内存空间但是该函数未向程序员要求分配内存而是在内部申请了内存保存了字符串。也就是说调用了该函数候要立即把信息复制到其他内存空间。因为若再次调用inet_ntoa函数则有可能覆盖之前保存的字符串信息 总之再次调用 inet_ntoa 函数前返回的字符串地址是有效的。若需要长期保存则应该将字符串复制到其他内存空间
给出该函数的调用示例
#include stdio.h
#include string.h
#include arpa/inet.hint main(int argc, char *argv[])
{struct sockaddr_in addr1, addr2;char *str_ptr;char str_arr[20];addr1.sin_addr.s_addrhtonl(0x1020304);addr2.sin_addr.s_addrhtonl(0x1010101);str_ptrinet_ntoa(addr1.sin_addr);strcpy(str_arr, str_ptr);printf(Dotted-Decimal notation1: %s \n, str_ptr);inet_ntoa(addr2.sin_addr);printf(Dotted-Decimal notation2: %s \n, str_ptr);printf(Dotted-Decimal notation3: %s \n, str_arr);return 0;
}
网络地址初始化
服务器端套接字创建过程中常见的网络地址信息初始化方法
struct sockaddr_in addr;
char *serv_ip 211.217.168.13; // 声明 IP 地址字符串
char *serv_port 9190; // 声明端口号字符串
memset(addr, 0, sizeof(addr)); // 结构体变量 addr 的所有成员初始化为 0主要是为了将 sockaddr_in 的成员 sin_zero 初始化为 0。
addr.sin_family AF_INET; // 指定地址族
addr.sin_addr.s_addr inet_addr(serv_ip); // 基于字符串的 IP 地址初始化
addr.sin_port htons(atoi(serv_port)); // 基于字符串的端口号初始化INADDR_ANY
初始化地址信息
struct sockaddr_in addr;
char * serv_port 9190;
memset(addr, 0, sizeof(addr));
addr.sin_family AF_INET;
add.sin_addr.s_addr htonl(INADDR_ANY);
addr.sin_port htons(atoi(serv_port));与之前方式最大的区别在于利用INADDR_ANY分配服务器端的IP地址若采用这种方式则可自动获取运行服务器端的计算机IP地址不必亲自输入。而且若同一计算机中已分配多个IP地址则只要端口号一致就可以从不同IP地址接收数据。
向套接字分配网络地址
前面了解了sockaddr_in结构体的初始化方法接下来就把初始化的地址信息分配给套接字。bind函数负责这项操作
#includesys/socket.h
int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);成功时返回0失败时返回-1。参数1sockfd要分配地址信息IP地址和端口号的套接字文件描述符参数2myaddr存有地址信息的结构体变量地址值。参数3addrlen第二个结构体变量的长度。如果此函数调用成功则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。
int serv_sock;
struct sockaddr_in serv_addr;
char * srev_port 9190;/* 创建服务器端套接字监听套接字*/
serv_sock socket(PF_INET, SOCK_STREAM, 0);/* 地址信息初始化 */
memset(serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family AF_INET;
serv_addr.sin_addr.s_addr htonl(INADDR_ANY);
serv_addr.sin_port htons(atoi(serv_port));/* 分配地址信息 */
bind(serv_sock, (struct sockaddr * )serv_addr, sizeof(serv_addr));服务器端代码结构如上
总结
这是《TCP/IP网络编程》专栏的第三篇文章欢迎各位读者订阅
更多资料点击 GitHub 欢迎各位读者去Star
⭐学术交流群Q 754410389 持续更新中~~~