网络编程之 字节序和深入理解bind()函数

简介: 在上一篇博客里,大家也许会对htons()感到疑惑吧,其实就是字节序的转换,所以这篇博客我们就来详细的解释一下什么是字节序。

在上一篇博客里,大家也许会对htons()感到疑惑吧,其实就是字节序的转换,所以这篇博客我们就来详细的解释一下什么是字节序。

也就是让大家对 bind函数有更加深刻而性感的认识

计算机是小端字节序,网络中是大端字节序。

      内存中的多字节数据相对于内存地址有大端和小端之分。


计算机有两种储存数据的方式 : 大端字节序 和 小端字节序。

"大端字节序":

          高位字节在前,低位字节在后,这也就和我们人类读写数字的方式是相同的。

"小端字节序":

          低位字节在前,高位字节在后,小端字节序和大端字节序是相反的。

那么博主就给大家举一个例子吧。


比如说:

这就是大端字节序和小端字节序的存放了。

20191030192303104.png

如果大家还是不能理解那么博主只能动用自己的表情包了!!!

20191030193513541.png

看完这个图肯定还是会有同学有疑惑?为什么需要字节序?那为什么字节序还要区分大小呢??


Q:大家是不是会疑惑???明明大端字节序已经才是我们正常生活中的啊??为啥要在搞一个小端字节序出来,这不是自找麻烦吗???

A:要详细解释这个就要牵扯到计算机原理了,简单来说就是为了效率(因为计算都是从低位开始的),计算机电路是优先处理低位字节的。而我们图中已经说的很明白了,我们的字节序是从高位到低位来排列的,如果正常读取

(计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读第一个字节,再读第二个字节)

会出现错误,而在将数据反过来读取效率又太低,所以就出现了

小端字节序。

希望大家明白,除了计算机的内部是小端字节序外,其他的基本都是大端字节序.

对于字节序的处理,大家只要记住只有读取的时候,才必须区分字节序,其他情况都不用考虑所以大家现在明白了吧,在我们输入了Ip和端口号之后,计算基会给我们处理成小端字节序,所以此时我们就要将小端字节序转换成大端字节序了。也就会用到我们的 htons()函数


为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数将

主机字节序(小端字节序)转换成网络字节序(大端字节序),或者将网络字节序转换成主机字节序

#include <arpa/inet.h>
//主机字节序(小端字节序)转换成网络字节序(大端字节序)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//网络字节序(大端字节序)转换成主机字节序(小段字节序)
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示"host",n表示network,l表示32位长整数,s表示16位短整数。

相信大家对字节序的概念已经有很性感的认识了。

所以我们再看一看 bind()函数那一整块代码

struct sockaddr_in servaddr;  //为了让bind()绑定IP和端口号而定义的
bzero(&serv_addr, sizeof(serv_addr)); //将网络地址清空
memset(&serv_addr,0,sizeof(serv_addr));//z这个方法也可以
servaddr.sin_family = AF_INET;//与socket()的第一个参数 int domain一样的协议
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定IP
servaddr.sin_port = htons(6666);  //绑定端口号
bind(serv_addr, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

是不是看到了熟悉的 htons() 和 htonl()了???


可是这个我们还是不知道啊 INADDR_ANY??

这个其实就与我们 bind()的第二个参数有关了不知道大家忘记没有,第二个参数有两个结构体哟,如果记不住的话博主在把之前的图拿出来给大家看一看

20191030200127706.png

看到了吧??存放32位的IP地址是不是有那么一点儿感觉了??哈哈没错其实就是 htonl()转换后的32位放到哪个结构体成员里面的。

INADDR_ANY:

2019103020314343.png

相信大家再看到bind()函数应该很舒服了吧。

struct sockaddr_in servaddr;  //为了让bind()绑定IP和端口号而定义的
bzero(&serv_addr, sizeof(serv_addr)); //将网络地址清空
memset(&serv_addr,0,sizeof(serv_addr));//z这个方法也可以
servaddr.sin_family = AF_INET;//与socket()的第一个参数 int domain一样的协议
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定IP
servaddr.sin_port = htons(6666);  //绑定端口号
bind(serv_addr, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//第三个参数就是第二个参数的大小
至于为什么要强制转化我在上一篇博客已经说了,就不再赘述了。

也许会有同学问,如果我想要绑定固定的IP地址呢???我不想用 INADDR_ANY。那么就要用到我们另外的函数了----->IP地址转换函数

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);

下面是书上对 IP地址转换函数的应用。

那里面的 argv[1]就是你自己输入的 IP地址

20191030205328842.png

大家是不是又看到了陌生的东西了吧??


htons(atoi(argv[2]))??? htons()我倒是知道哦,但是 atoi()又是啥呀?? 其实很容易的,你看看前面是 serv_addr.sin.port所以是端口号,你在输入端口号和IP时,其实输入的是字符串,而atoi()函数的作用就是把字符串转换成整型数的一个函数。

//小技巧相比于上文介绍的字节序转换函数:
如果要使用特定的IP的对比
#include <arpa/inet.h>
//主机字节序(小端字节序)转换成网络字节序(大端字节序)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//网络字节序(大端字节序)转换成主机字节序(小段字节序)
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示"host",n表示network,l表示32位长整数,s表示16位短整数。
//还有一种转换方式:
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
支持IPv4和IPv6
可重入函数
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr。
因此函数接口是void *addrptr。
//二者的区别:
"192.168.1.24" -->unsigned int --> htonl() --> 网络字节序
"192.168.1.24" ------------------> 网络字节序  inet_pton();
网络字节序 ------------------> 点分十进制字符串  inet_ntop();
也就是 inet_pton();可以直接将IP字符串转换成网络字节序。而在使用htonl();之前还需要将IP字符串转换成无符号整数之后才行(转换方法atoi()上面有所提及)。
如果直接用 INADDR_ANY 这个宏(他本身就是无符号整数)所以直接用htonl即可。

好啦,bind()函数讲到这里相信大家一定不会再感到陌生了。下面再看一看bind()函数吧,最后一次加强印象了。

struct sockaddr_in servaddr;  //为了让bind()绑定IP和端口号而定义的
bzero(&serv_addr, sizeof(serv_addr)); //将网络地址清空
memset(&serv_addr,0,sizeof(serv_addr));//这个方法也可以  二选一
servaddr.sin_family = AF_INET;//与socket()的第一个参数 int domain一样的协议
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //绑定IP
servaddr.sin_port = htons(6666);  //绑定端口号
bind(serv_addr, (struct sockaddr*)&serv_addr, sizeof(serv_addr));


                谢谢观看

目录
相关文章
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
用MASM32按Time Protocol(RFC868)协议编写网络对时程序中的一些有用的函数代码
|
1月前
|
机器学习/深度学习 编解码
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
本文介绍了九种常用的神经网络激活函数:Sigmoid、tanh、ReLU、ReLU6、Leaky ReLU、ELU、Swish、Mish和Softmax,包括它们的定义、图像、优缺点以及在深度学习中的应用和代码实现。
130 0
深度学习笔记(三):神经网络之九种激活函数Sigmoid、tanh、ReLU、ReLU6、Leaky Relu、ELU、Swish、Mish、Softmax详解
|
1月前
|
机器学习/深度学习 数据可视化 算法
激活函数与神经网络------带你迅速了解sigmoid,tanh,ReLU等激活函数!!!
激活函数与神经网络------带你迅速了解sigmoid,tanh,ReLU等激活函数!!!
|
2月前
|
Linux C语言 Windows
C语言 网络编程(六)字节序
本文介绍了在不同操作系统中查看IP地址和网络状态的方法,包括Windows下的`ipconfig`与Linux下的`ifconfig`命令,并详细解析了网络字节序转换函数。通过`inet_aton()`和`inet_addr()`可将IP字符串转换为网络字节序,而`inet_ntoa()`则实现相反操作。此外,还提供了`htons()`与`ntohs()`等函数进行主机字节序与网络字节序之间的转换,并附带示例代码帮助理解。
|
3月前
|
机器学习/深度学习 算法
神经网络中激活函数的重要性
【8月更文挑战第23天】
33 0
|
3月前
|
监控
【网络编程】poll函数
【网络编程】poll函数
30 0
|
3月前
|
监控
【网络编程】select函数
【网络编程】select函数
66 0
|
3月前
|
机器学习/深度学习 Shell 计算机视觉
一文搞懂 卷积神经网络 卷积算子应用举例 池化 激活函数
这篇文章通过案例详细解释了卷积神经网络中的卷积算子应用、池化操作和激活函数,包括如何使用卷积算子进行边缘检测和图像模糊,以及ReLU激活函数如何解决梯度消失问题。
|
4月前
|
机器学习/深度学习
神经网络可能不再需要激活函数?Layer Normalization也具有非线性表达!
【7月更文挑战第14天】研究表明,层归一化(LayerNorm)可能具备非线性表达能力,挑战了神经网络对激活函数的依赖。在LN-Net结构中,仅使用线性层与LayerNorm就能实现复杂分类,其VC维度下界证明了非线性表达。尽管如此,是否能完全替代激活函数及如何有效利用这一特性仍需更多研究。[arXiv:2406.01255]
59 5
|
4月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于负相关误差函数的4集成BP神经网络matlab建模与仿真
**算法预览:** 图像显示无水印的2022a版MATLAB运行结果 **软件版本:** MATLAB 2022a **核心代码片段:** 省略展示 **理论概述:** NCL集成BP网络利用负相关提升泛化,结合多个弱模型减少错误关联。通过λ参数控制模型间负相关程度,λ&gt;0增强集成效果,提高预测准确性和系统稳健性。
下一篇
无影云桌面