TCP IP网络编程(三) 地址族与数据序列
文章目录
- 分配给套接字的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-127
- B类地址的首字节范围:128-191
- C类地址的首字节范围: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_t | signed 8-bit int | sys/types.h |
uint8_t | unsigned 8-bit int | sys/types.h |
int16_t | signed 16-bit int | sys/types.h |
uint16_t | unsigned 16-bit int | sys/types.h |
int32_t | signed 32-bit int | sys/types.h |
uint32_t | unsigned 32-bit int | sys/types.h |
sa_family_t | 地址族(address family) | sys/socket.h |
socklen_t | 长度(length of struct) | sys/socket.h |
in_addr_t | IP地址,,uint32_t | netinet/in.h |
in_port_t | 端口号,uint16_t | netinet/in.h |
结构体sockaddr_in 的成员分析
成员 sin_family
每种协议族适用的协议族均不同,比如IPv4使用4字节地址族,IPv6使用16字节地址族
地址族
地址族 | 含义 |
---|---|
AF_INET | IPv4网络协议中使用的地址族 |
AF_INET6 | IPv6网络协议中使用的地址族 |
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 指的是short
- htons中的 l 指的是long(Linux中long类型占用4个字节)
#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short host_port=0x1234;unsigned short net_port;unsigned long host_addr=0x12345678;unsigned long net_addr;net_port=htons(host_port);net_addr=htonl(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位整数型数据,在转换类型的同时进行网络字节序转换。
#include<arpa/inet.h>
in_addr_t inet_addr(const char * string);//成功时返回32位大端序整数型值,失败时返回INADDR_NONE。
该函数的调用过程
#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{char *addr1="127.212.124.78";char *addr2="127.212.124.256";unsigned long conv_addr=inet_addr(addr1);if(conv_addr==INADDR_NONE)printf("Error occured! \n");elseprintf("Network ordered integer addr: %#lx \n", conv_addr);conv_addr=inet_addr(addr2);if(conv_addr==INADDR_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结构体,且使用频率更高。
#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);成功时返回1,失败时返回0参数1:string,含有需转换的IP地址信息的字符串地址值。参数2:addr,将保存转换结果的in_addr结构体变量的地址值。
该函数的调用过程
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);int main(int argc, char *argv[])
{char *addr="127.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.h>int main(int argc, char *argv[])
{struct sockaddr_in addr1, addr2;char *str_ptr;char str_arr[20];addr1.sin_addr.s_addr=htonl(0x1020304);addr2.sin_addr.s_addr=htonl(0x1010101);str_ptr=inet_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函数负责这项操作
#include<sys/socket.h>
int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);成功时返回0,失败时返回-1。参数1:sockfd,要分配地址信息(IP地址和端口号)的套接字文件描述符参数2:myaddr,存有地址信息的结构体变量地址值。参数3:addrlen,第二个结构体变量的长度。
如果此函数调用成功,则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。
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 持续更新中~~~
相关文章:
TCP IP网络编程(三) 地址族与数据序列
文章目录 分配给套接字的IP地址与端口号网络地址网络地址分类与主机地址边界 地址信息的表示表示 IPv4地址的结构体结构体sockaddr_in 的成员分析 网络字节序与地址变换字节序与网络字节序字节序转换 网络地址的初始化与分配将字符串信息转换为网络字节序的整数型网络地址初始化…...

对比Flink、Storm、Spark Streaming 的反压机制
分析&回答 Flink 反压机制 Flink 如何处理反压? Storm 反压机制 Storm反压机制 Storm 在每一个 Bolt 都会有一个监测反压的线程(Backpressure Thread),这个线程一但检测到 Bolt 里的接收队列(recv queue)出现了…...
Ubuntu常用配置集合
Ubuntu配置软件镜像源 参考文章:Ubuntu如何配置软件镜像源 建议使用清华的源。 Ubuntu安装SSH服务: 参考文章:Ubuntu安装SSH服务 ubuntu下安装使用nvm 参考文章:ubuntu下安装使用nvm 出现下载sh文件不成功的情况,…...
传统三维重建和深度学习三维重建 MVS笔记总结、问题总结
什么是cost-volume ?(代价体) 什么是置信度?置信区间? pixel-wise,patch-wise,image-wise的区别 图像 4领域-8领域-16领域 及代码实现 文章目录 1 plane-sweeping2 传统三维重建深度学习三维重建有何不同呢?3 大型场景重建4 PMVS-精确、密集、鲁棒的多视图立体视觉…...

Ansible学习笔记10
1、在group1的被管理机里的mariadb里创建一个abc库; 1) 然后我们到agent主机上进行检查: 可以看到数据库已经创建成功。 再看几个其他命令: #a组主机重启mysql,并设置开机自启 ansible a -m service -a "namemy…...

肖sir__linux详解__002(系统命令)
linux系统命令 1、df 查看磁盘使用情况 (1)df 查看磁盘使用情况(按kb单位显示) (2)df -h 按单位显示磁盘使用情况 2、top 实时查看动态进程 (1)top 详解: 第一行&…...

AI绘画:StableDiffusion实操教程-斗罗大陆2-江楠楠-常服(附高清图下载)
前段时间我分享了StableDiffusion的非常完整的教程:“AI绘画:Stable Diffusion 终极宝典:从入门到精通 ” 尽管如此,还有读者反馈说,尽管已经成功安装,但生成的图片与我展示的结果相去甚远。真实感和质感之…...

JavaScript运行机制与实践应用
一、JavsScript运行机制 1、JavaScript 是一种解释型语言,它的执行机制主要包括以下几个步骤: 2、事件循环 3、JavaScript运行模型 4、JavaScript任务 5、JavaScript宏任务和微任务 6、案例分析 console.log(script start) setTimeout(function () {co…...

【算法奥义】最大矩形问题
首先建立一个二维数组,这个二维数组,计算出矩阵的每个元素的左边连续 1 的数量,使用二维数组 left记录,其中left[i][j] 为矩阵第 i 行第 j 列元素的左边连续 1 的数量。 也就是从这个元素开始,从右往左边数有多少个连…...
06 Kafka线上集群部署方案
kafka部署在linux上有什么好处 网络传输效率 kafka部署在linux上,可以用到linux的零拷贝提升网络传输效率,提高kafka的吞吐量。利用零拷贝可以使数据不经过用户态直接通过网卡发送给接收方,实现数据的高性能传输 kafka和零拷贝技术 kafka…...
flex-shrink计算题
当我们使用 flexbox 布局时,flex-shrink 属性用于指定 flex 项在空间不足时收缩的比例。它表示了一个 flex 项相对于其他 flex 项收缩的比例。 假设有一个 flex 容器,其中包含三个子项,它们的 flex-shrink 分别设置为 1、2 和 3。当容器的可…...
Springboot - 5.Bean的生命周期
✍1. Bean的生命周期: 当然,我会详细描述每一步的作用。 🎷1. 实例化Bean: 这是Bean生命周期的第一步。Spring容器通过反射机制创建Bean的实例。public class ExampleBean {// ... }🎷2. 设置Bean的属性: Spring容器将根据配置…...

华为云 sfs 服务浅谈
以root用户登录弹性云服务器。 以root用户登录弹性云服务器。 安装NFS客户端。 查看系统是否安装NFS软件包。 CentOS、Red Hat、Oracle Enterprise Linux、SUSE、Euler OS、Fedora或OpenSUSE系统下,执行如下命令: rpm -qa|grep nfs Debian或Ubuntu系统下…...

CSS中如何实现元素的渐变背景(Gradient Background)效果?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ CSS 渐变背景效果⭐ 线性渐变背景⭐ 径向渐变背景⭐ 添加到元素的样式⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&…...

buildroot修改内核防止清理重新加载办法
当你使用 Buildroot 构建 Linux 内核时,如果对内核文件进行了手动修改,重新执行 Buildroot 的构建过程将会覆盖你所做的修改。这是因为 Buildroot会根据配置重新下载、提取和编译内核。 为了避免在重新构建时覆盖你的修改,可以采取以下两种方…...

Vue框架--Vue中的事件
1.事件处理 事件的基本使用: (1).使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名; (2).事件的回调需要配置在methods对象中,最终会在vm上; (3).methods中配置的函数,不要用箭头函数!否则this就不是vm了; (4).methods中配置的函数,都是被Vue所管理的函数,this的…...
1921. 消灭怪物的最大数量
原题地址 解法一 排序贪心即可。思想为先计算出每一个怪兽到达城市的时间,然后排序,有小到大进行消灭,此时的下标可视作时间。当怪兽到达城市的时间超过或等于当前时间时,即已经到达了城市,游戏失败,下标…...

创建一个空的vue项目,配置及步骤
查看需要的环境及插件版本 创建vue命令 默认配置 手动配置 其他 hash和history的区别: hash 模式,url后,会带着#,改变hash,页面不会刷新,不会更改整个页面,只会更改#后面路由配置的内容&#x…...

一篇文章教会你如何编写一个简单的Shell脚本
文章目录 简单Shell脚本编写1. 简单脚本编写2. Shell脚本参数2.1 Shell脚本参数判断2.1.1 文件测试语句2.1.2 逻辑测试语句2.1.3 整数值测试语句2.1.4 字符串比较语句 3. Shell流程控制语句3.1 if 条件测试语句3.1.1 if...3.1.2 if...else...3.1.3 if...elif...else 4. Shell脚…...
SSM框架-spring
SSM框架参考 spring...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...