一、为什么必须做类型 / 字节序转换?
- 人类用的是:IP字符串 (如
192.168.1.1) - 网络传输用的是:
uint32_t整数 - 本机用的是:主机字节序(小端)
- 网络协议用的是:网络字节序(大端)
所以:数据进网络之前必须转,出网络之后必须转回来。
二、核心转换函数
| 函数 | 英文全名 | 中文含义 | 作用 |
|---|---|---|---|
| htons | Host TO Network Short | 主机 -> 网络(短整型 16 位) | 端口号发送前转换 |
| ntohs | Network TO Host Short | 网络 -> 主机(短整型 16 位) | 端口号接收后解析 |
| htonl | Host TO Network Long | 主机 -> 网络(长整型 32 位) | IP 整数发送前转换 |
| ntohl | Network TO Host Long | 网络 -> 主机(长整型 32 位) | IP 整数接收后解析 |
三、UDP转换总结
1. 端口号转换(16 位整数)
主机发给网络 → 用 htons (Host TO Network Short) 主机端口 → 网络字节序( 短整型 16 位**)**
cpp
//服务端中,绑定bind服务端进程的端口号
local.sin_port = htons(port_);
//客户端中,把目标服务器端口serverport放入struct sockaddr_in server中;
server.sin_port = htons(serverport);
网络发给主机 → 用 ntohs(Network TO Host Short) 网络**字节序** -> 主机**端口**(短整型 16 位)
cpp
//服务器获得客户端的端口号
uint16_t clientport = ntohs(client.sin_port);
2. IP 地址转换(string ↔ uint32_t)
网络序整数 IP 转回字符串 IP , 用 inet_ntoa( Network TO Address**) **
cpp
//服务端接收(recvfrom)客户端client中获得客户端ip
std::string clientip = inet_ntoa(client.sin_addr);
字符串 IP 转变类型为网络序整数 IP , 用 inet_addr( Address**) **
cpp
//服务端中,绑定(bind)服务端ip
server.sin_addr.s_addr = inet_addr(serverip.c_str());
3. 服务端 INADDR_ANY (INADDR_ANY = 0.0.0.0)主机字节序 特殊转换
用 htonl (Host TO Network Long) 转成网络字节序
cpp
//服务端中,绑定(bind)任意服务端ip
local.sin_addr.s_addr = htonl(INADDR_ANY);
四、完整转换对应表
| 功能 | 函数 | 英文全名 | 代码位置 |
|---|---|---|---|
| 主机端口 → 网络端口 | htons | Host To Network Short | 客户端、服务端 bind |
| 网络端口 → 主机端口 | ntohs | Network To Host Short | 服务端解析客户端 |
| 字符串 IP → 网络整数 IP | inet_addr | Internet Address | 客户端填充 server |
| 网络整数 IP → 字符串 IP | inet_ntoa | Internet Network To Address | 服务端打印客户端 IP |
| 0.0.0.0 转网络序 | htonl | Host To Network Long | 服务端 bind |
| 结构体类型强转 | (struct sockaddr*)&... | - | bind / recvfrom / sendto |
六、总结
- 端口一定用
htons/ntohs - IP 字符串一定用
inet_addr/inet_ntoa - INADDR_ANY 必须
htonl - 所有系统调用必须强转
sockaddr结构体