端口号(port)是传输层协议的内容。
A主机把数据发送给B主机是目的吗?
不是的,这只是一种手段,我们其实是要把A主机上的数据交给B主机上的某种服务。(本质是某种客户端和某种服务器)
我们平时想看抖音短视频必须打开抖音APP,打开抖音APP就是启动抖音的客户端,本质是一个进程;同时我们能在任意时间刷短视频,是因为抖音的服务器在 7*24小时内以进程的方式在运行;所以我们所有的请求动作不是由主机A发起的,而是由主机A上的客户端进程发起的,后与主机B上的服务器进程进行通信。
网络通信本质:其实是进程间通信
通信的两个阶段:
先将数据通过OS,将数据发送到目标主机(手段)
再在本机收到的数据,推送给自己上层的指定进程
IP地址 + 端口号 = 互联网中唯一的一个进程 (目前)
[源IP,源端口号;目的IP,目的端口] 可以标识互联网中唯二的两个进程,我们把这种通信方式叫做: socket通信
网络通信的本质:是通过IP地址 + 端口号构建进程的唯一性,来进行基于网络的进程间通信IP
问题:
端口号,为什么不用进程PID来标识进程的唯一性?
一个端口号只能被一个进程绑定,一个进程可以关联多个端口号
理解: 在OS层面上可以维护一张哈希表,进程在绑定port时就是把PID填入哈希表中,哈希表中下标唯一,一个PID可以对应很多下标;所以在数据报到来时,OS可以根据报文的目的端口号查哈希表找到对应进程,未来把数据交给进程
传输层协议(TCP和UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号. 就是在描述 “数据是谁发的, 要
发给谁”;
此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识; 后面我们再详细讨论TCP的一些细节问题。
此处我们也是对UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识; 后面再详细讨论。
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
说明: 参数在使用时在详细解释
上面是一套网络通信的接口,sockaddr是这些接口中最重要的参数
sockaddr_in:是用于网络通信的套接字,
sockaddr_un:是用于域间通信(本地通信)的套接字,用来取代system V原始通信的方案
sockaddr:是一套通用的接口,既可以本地通信又可以网络通信
但是我们在使用函数的时候,传的参数是sockaddr,而不是sockaddr_in和sockaddr_un
传不同的结构体,用相同的参数来接受,在函数内部自动进行区分,这是用C语言实现多态的一种做法