大小端、字节顺序转换函数和IP地址格式转换函数

  • 大小端
    • CPU大小端之分
    • 常见字节序
  • 字节顺序转换函数
    • Linux系统下定义
    • Windows系统下
      • ntohs()
      • htons()
      • htonl()和ntohl()
  • IP地址格式转换函数
    • inet_aton、inet_addr、inet_ntoa函数(已废弃)
    • inet_ntop、inet_pton函数
      • inet_pton()函数
      • inet_ntop()函数
      • 示例

大小端

CPU大小端之分

大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中(高存低,低存高)

小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中(高存高,低存低)

假如32位宽(uint32_t)的数据0x12345678,从地址0x08004000开始存放:

再结合一张图进行理解:

常见字节序

常见的操作系统是小端,通讯协议是大端。

常见CPU的字节序
大端模式:PowerPC、IBM、Sun
小端模式:x86、DEC

ARM既可以工作在大端模式,也可以工作在小端模式。STM32属于小端模式。

大小端应用的地方很多,如通信协议、数据存储等。如果字节序不一致,就需要转换。这就引出下面网络编程中的四个函数

字节顺序转换函数

ntohs、ntohl、htons和htonl含义如下:
ntohs =net to host short int 16位
htons=host to net short int 16位
ntohl =net to host long int 32位
htonl=host to net long int 32位

网络字节顺序NBO(Network Byte Order):
按从高到低的顺序存储,在网络上使用同一的网络字节顺序,可避免兼容性问题(大端模式)

主机字节顺序HBO(Host Byte Order):
不同的机器HBO不相同,与CPU的设计有关,数据的顺序是由CPU决定的,而与操作系统无关;

如Intel x86结构下(小端模式),short型数0x1234表示为34 12,int型数0x12345678表示为78 56 34 12;

如IBM power PC结构下(大端模式),short型数0x1234表示为 12 34,int型数0x12345678表示为 12 34 56 78.

由于这个原因,不同体系结构的机器之间不能直接通信,所以要转换成一种约定的顺序,也就是网络字节顺序,其实就是如同power pc那样的顺序。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换

Linux系统下定义

#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);

Windows系统下

ntohs()

将一个无符号短整形数从网络字节顺序转换为主机字节顺序。

#include <winsock.h>u_short PASCAL FAR ntohs( u_short netshort);
/*
netshort:一个以网络字节顺序表达的16位数。
注释:本函数将一个16位数由网络字节顺序转换为主机字节顺序。
返回值:ntohs()返回一个以主机字节顺序表达的数。
*/

htons()

将主机的无符号短整形数转换成网络字节顺序。

#include <winsock.h>u_short PASCAL FAR htons( u_short hostshort);
/*
hostshort:主机字节顺序表达的16位数。
注释:本函数将一个16位数从主机字节顺序转换成网络字节顺序。
返回值:htons()返回一个网络字节顺序的值。
*/

以上2个函数提供了主机字节顺序与网络字节顺序的转换,比如网络字节为00 01,u_short a。如何直接对应,a=0100; 为什么呢?因为主机是从高字节到低字节的,所以应该转化后a=ntohs(0001); 这样 a=0001

htonl()和ntohl()

htonl()表示将32位的主机字节顺序转化为32位的网络字节顺序,htons()表示将16位的主机字节顺序转化为16位的网络字节顺序(ip地址是32位的端口号是16位的)

ntohl()表示将32位的网络字节顺序转化为32位的主机字节顺序,ntohs()表示将16位的网络字节顺序转化为16位的主机字节顺序(ip地址是32位的端口号是16位的)

IP地址格式转换函数

对于人来说,我们更容易阅读的是点分十进制的 IP 地址,譬如 192.168.1.110、192.168.1.50,这其实是一种字符串的形式,但是计算机所需要理解的是二进制形式的 IP 地址,所以我们就需要在点分十进制字符串和二进制地址之间进行转换。

点分十进制字符串和二进制地址之间的转换函数主要有:inet_aton、inet_addr、inet_ntoa、inet_ntop、inet_pton 这五个,在我们的应用程序中使用它们需要包含头文件<sys/socket.h>、<arpa/inet.h>以及<netinet/in.h>。

inet_aton、inet_addr、inet_ntoa函数(已废弃)

这些函数可将一个 IP 地址在点分十进制表示形式和二进制表示形式之间进行转换,这些函数已经废弃了,基本不用这些函数了,但是在一些旧的代码中可能还会看到这些函数。完成此类转换工作我们应该使用下面介绍的这些函数。

inet_ntop、inet_pton函数

inet_ntop()、inet_pton()与 inet_ntoa()、inet_aton()类似,但它们还支持IPv6地址。它们将二进制Ipv4或Ipv6地址转换成以点分十进制表示的字符串形式,或将点分十进制表示的字符串形式转换成二进制Ipv4或Ipv6地址。使用这两个函数只需包含<arpa/inet.h>头文件即可

inet_pton()函数

inet_pton()函数原型如下所示:

int inet_pton(int af, const char *src, void *dst);

inet_pton()函数将点分十进制表示的字符串形式转换成二进制 Ipv4 或 Ipv6 地址。

将字符串 src 转换为二进制地址,参数 af 必须是 AF_INET 或 AF_INET6,AF_INET 表示待转换的 Ipv4地址,AF_INET6 表示待转换的是 Ipv6 地址;并将转换后得到的地址存放在参数 dst 所指向的对象中,如果参数 af 被指定为 AF_INET,则参数 dst 所指对象应该是一个 struct in_addr 结构体的对象;如果参数 af 被指定为 AF_INET6,则参数 dst 所指对象应该是一个 struct in6_addr 结构体的对象。

inet_pton()转换成功返回 1(已成功转换)。如果 src 不包含表示指定地址族中有效网络地址的字符串,则返回 0。如果 af 不包含有效的地址族,则返回-1 并将 errno 设置为 EAFNOSUPPORT。

inet_ntop()函数

inet_ntop()函数执行与 inet_pton()相反的操作,函数原型如下所示:

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数 af 与 inet_pton()函数的 af 参数意义相同。

参数 src 应指向一个 struct in_addr 结构体对象或 struct in6_addr 结构体对象,依据参数 af 而定。函数inet_ntop()会将参数 src 指向的二进制 IP 地址转换为点分十进制形式的字符串,并将字符串存放在参数 dts所指的缓冲区中,参数 size 指定了该缓冲区的大小。

inet_ntop()在成功时会返回 dst 指针。如果 size 的值太小了,那么将会返回 NULL 并将 errno 设置为
ENOSPC。

示例

Linux系统下
tcpclient.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>#define SERVER_PORT 8888 //服务器的端口号
#define SERVER_IP "192.168.31.200" //服务器的 IP 地址int main(void)
{struct sockaddr_in server_addr = {0};char buf[512];int sockfd;int ret,ret1;int a[5]={1,2,3,4,5};/* 打开套接字,得到套接字描述符 */sockfd = socket(AF_INET, SOCK_STREAM, 0);if (0 > sockfd){perror("socket error");exit(EXIT_FAILURE);}/* 调用 connect 连接远端服务器 */server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT); //端口号inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP 地址ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (0 > ret){perror("connect error");close(sockfd);exit(EXIT_FAILURE);}printf("服务器连接成功...\n\n");/* 向服务器发送数据 */for ( ; ; ){// 清理缓冲区memset(buf, 0x0, sizeof(buf));printf("开始发数组a[5]\n"); ret1 = send(sockfd, a, sizeof(a), 0);if(0 > ret1){perror("send error");break;}printf("数组a[5]发送结束\n"); // 接收用户输入的字符串数据printf("Please enter a string: ");fgets(buf, sizeof(buf), stdin);// 将用户输入的数据发送给服务器ret = send(sockfd, buf, strlen(buf), 0);if(0 > ret){perror("send error");break;}//输入了"exit",退出循环if(0 == strncmp(buf, "exit", 4))break;}close(sockfd);exit(EXIT_SUCCESS);
}

tcpserver.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>#define SERVER_PORT 8888 //端口号不能发生冲突,不常用的端口号通常大于 5000int main(void)
{struct sockaddr_in server_addr = {0};struct sockaddr_in client_addr = {0};char ip_str[20] = {0};int sockfd, connfd;int addrlen = sizeof(client_addr);char recvbuf[512];int b[5];int ret,ret1;/* 打开套接字,得到套接字描述符 */sockfd = socket(AF_INET, SOCK_STREAM, 0);if (0 > sockfd) {perror("socket error");exit(EXIT_FAILURE);}/* 将套接字与指定端口号进行绑定 */server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(SERVER_PORT);ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (0 > ret) {perror("bind error");close(sockfd);exit(EXIT_FAILURE);}/* 使服务器进入监听状态 */ret = listen(sockfd, 50);if (0 > ret) {perror("listen error");close(sockfd);exit(EXIT_FAILURE);}/* 阻塞等待客户端连接 */connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);if (0 > connfd) {perror("accept error");close(sockfd);exit(EXIT_FAILURE);}printf("有客户端接入...\n");inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));printf("客户端主机的 IP 地址: %s\n", ip_str);printf("客户端进程的端口号: %d\n", client_addr.sin_port);/* 接收客户端发送过来的数据 */for ( ; ; ) {// 接收缓冲区清零memset(recvbuf, 0x0, sizeof(recvbuf));ret1 = recv(connfd, b, sizeof(b), 0);if(0 >= ret1) {perror("recv a[5] error");close(connfd);break;}printf("from client int: %d,%d,%d\n", b[0],b[1],b[2],b[3],b[4]);// 读数据ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);if(0 >= ret) {perror("recv error");close(connfd);break;}// 将读取到的数据以字符串形式打印出来printf("from client: %s\n", recvbuf);// 如果读取到"exit"则关闭套接字退出程序if (0 == strncmp("exit", recvbuf, 4)) {printf("server exit...\n");close(connfd);break;}}/* 关闭套接字 */close(sockfd);exit(EXIT_SUCCESS);
}

大小端、字节顺序转换函数和IP地址格式转换函数相关推荐

  1. Java字节序与大小端转换_什么时候要进行大小端字节序的转换? | 学步园

    什么时候要进行大小端字节序的转换? 通过socket收发数据(在intel的处理器上), 什么时候要进行大小端字节序的转换? 比如发一个int型的数据出去,要不要进行转换? 还是两端一样,就不用考虑转 ...

  2. 什么时候要进行大小端字节序的转换?

    什么时候要进行大小端字节序的转换? 通过socket收发数据(在intel的处理器上), 什么时候要进行大小端字节序的转换? 比如发一个int型的数据出去,要不要进行转换? 还是两端一样,就不用考虑转 ...

  3. 大小端字节序知识详解

     计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian). 举例: 数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11. 大 ...

  4. 通信大小端字节序相关问题总结

    [备注:本文主要是将最新box项目中,遇到的问题做以总结梳理,便于加深印象和后续查阅.本文相关资料主要来源于网络,包括转载的一些资料] 遇到问题: 1.何为大.小端存储. 2.若协议中提及" ...

  5. 大小端字节序介绍以及判断当前环境字节序的程序【C语言】

    文章目录 1.大小端字节序介绍 2. 判断当前环境的字节序程序 1.大小端字节序介绍 首先我们先来介绍什么是大端字节序,什么是小端字节序: 大小端字节序指的是数据在电脑上存储的字节顺序 小端字节序存储 ...

  6. java 大小端字节序_理解大小端字节序

    学过编程的人都应该知道大小端字节序的概念,但是很多时候,总是把他们弄混,这是整理出来的一份很简单的方式理解字节序的文章,废话不多说,这里直接入正题. 什么是字节序? 字节序,简单来说,就是指的超过一个 ...

  7. 【C语言】大小端字节序判断 常见笔试题型

    [前言]大家好,我是Catzzz666,一个一心让大家变强的博主.废话不说,让我们进入今天的正题. 大小端的引入: 我们先来看这样一段代码: 在内存中我们创建了局部变量a,那么a在内存中又是如何存储的 ...

  8. MySQL inet aton函数,MySQL IP转数字函数 INET_ATON() INET_NTOA()

    223 total views, 1 views today 在MySQL中IP地址可以使用varchar字段类型存储,但是若查找某个IP区间的IP地址就很麻烦. 但是通过 INET_ATON() 函 ...

  9. 网络编程大小端字节序

    刚接触网络编程那会因为是同一种语言之间做数据交互所以并没有出现大小端问题,到后来鄙人因机缘巧合进入物联网行业然后就发现打开了新世界.(设备终端大多是嵌入式设备用的C,服务端用的是java) 然后我的状 ...

最新文章

  1. dev c++自动补全_Flutter 自动化测试-开篇
  2. 等保2.0丨2021 必须了解的40个问题
  3. 虚拟机技术抗黑产!几维安全KiwiVM虚拟机实现全平台全架构防护方案!
  4. 5G,仅仅是更快的网速吗?
  5. 数据挖掘技术-主键合并数据准备数据
  6. 2019阿里秋招一道笔试题(关于火柴拼出最大数字) - Android开发岗
  7. java输出GPA_请完成下列Java程序:实现换算GPA,对于学生学习的每一门课程,都输入两个..._考试资料网...
  8. py218-基于Python+django的化妆品美妆销售商城网站#毕业设计
  9. swapidc鸟云模板开源版
  10. linux的cuda10卸载,Ubuntu18.04下卸载CUDA11.0
  11. oracle 空闲连接数_oracle数据库空闲连接
  12. 解决JS在controll层定义带循环的公共方法,组件中调用时取不到返回值的问题
  13. BusyBox安装只要两步(小米8)2020/3/13亲测
  14. RESTART-Axu1
  15. 让我们习惯在底层用C++宏生成代码
  16. After Effects Guru: Expressions After Effects 大师教程之表达式 Lynda课程中文字幕
  17. 新形势下幼儿园班级管理模式初探
  18. 项目治理 vs 组织级项目管理
  19. oracle11gr2 dbca建库,Oracle11gR2--手工建库dbca建库
  20. Vue实现城市定位(利用百度地图)

热门文章

  1. UG_NX的详细安装办法(亲测有效)(原创)
  2. ubuntu rename
  3. ATI显卡优酷,PPs看电影花屏或全屏卡的解决方法(未验证)
  4. FPS(刷新率)介绍
  5. 读《蔡康永的说话之道》-表现自己,体现别人
  6. Android手机App安全漏洞整理
  7. MySQL binlog日志恢复数据详细操作步骤
  8. session使用实例
  9. 超详细步骤,教你一次就成功注册Gmail邮箱(同时可以用于申请谷歌广告账户)
  10. sqlite原理分析和开发应用