一、前缀知识
端口号:用来标识在一台机器上的唯一进程。
IP地址:用来标识在网络上的唯一主机 。
因此通过IP+端口号的方式,就可以在互联网上唯一标识一个进程,套接字就是通过这种方式,唯一地标识在互联网之间通信的一对进程,网络套接字的本质其实就是进程间的通信。
套接字是在传输层的接口,可以让我们选择通信的协议是UDP,还是TCP。
1.1 端口号 & 进程PID
这里区分一下端口号和进程的PID,看到这里你也许会问:进程的PID也能标识进程的唯一性,为什么网络不用进程PID来标识进程的唯一性?
从技术角度上,用PID来标识进程的唯一性是完全可行的,而套接字通信通过端口号来标识进程唯一性,可以进行解耦,比如技术迭代更新之后以后进程PID出现了字母,特殊符号等等,这样子套接字就要重新实现里面的逻辑,而用端口号就能进行解耦。
这里要区分一下:一个进程可以对应多个端口号,而一个端口号只能对应一个进程。
二、网络字节序
内存中有很多字节序,这些字节序在不同的机器上存储的方式不用,有大端存储和小端存储的区分。每台机器大小端不相同会导致网络通信出现许多问题。
例如:A主机是大端存储,B主机是小端存储,那么如果A、B两台主机要进行通信,A接受到的数据应该按大端处理还是小端处理?B同理。
下面提供一段检测大小端的demo
int main()
{
//大端:00 00 00 01
//小端:01 00 00 00
int a = 1;
//只取第一个字节
char* pa = (char*)&a;
if (*pa == 1) std::cout << "小端存储" << std::endl;
else std::cout << "大端存储" << std::endl;
return 0;
}
因此,TCP/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);
- uint32_t:64位系统的 unsigned int 占8个字节,32位
- uint16_t:32位系统的 unsigned int 占4个字节,16位
- h表示host即主机,n表示net即网络,l表示long即32位,s表示short即16位
- htonl:传入一个64位的uint32_t类型,把他转化为大端(发送至网络)
- ntohl:传入一个64位的uint32_t类型,把他转化为小段(从网络接收)
- htons:传入一个32位的uint16_t类型,把他转化为大端&#