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

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


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




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



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

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

"大端字节序":
           高位字节在前,低位字节在后,这也就和我们人类读写数字的方式是相同的。
"小端字节序":
           低位字节在前,高位字节在后,小端字节序和大端字节序是相反的。

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

比如说:
这就是大端字节序和小端字节序的存放了。
在这里插入图片描述
如果大家还是不能理解那么博主只能动用自己的表情包了!!!

在这里插入图片描述

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

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()的第二个参数有关了不知道大家忘记没有,第二个参数有两个结构体哟,如果记不住的话博主在把之前的图拿出来给大家看一看

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

INADDR_ANY:在这里插入图片描述
相信大家再看到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地址
在这里插入图片描述

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

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));
								谢谢观看

在这里插入图片描述

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值