Socket编程预备理解传入数据的目的IP地址在网络中标记主机唯一性。源主机通过目标主机的IP地址将数据传输给目标主机。但将数据传输给主机是目的吗不是因为数据最终都是给人用的将数据传输给主机只是手段。比如QQ聊天其实是人在聊浏览网页数据其实是人在浏览。那么人应该如何获取对应的数据呢通过启动对应的应用程序获得比如想要获得聊天消息就要启动QQ等聊天程序想要获取网页数据就要启动浏览器。而对应的应用程序都是进程。换句话说进程就是人在计算机中的代表只要把数据交给对应的进程就相当于交给了人。所以数据传输到主机并不是目的而是手段最终将数据交给对应的进程才是目的认识端口号我们现在知道了数据传输给目标主机后最终目的是将数据交给对应的进程。但是计算机中有不计其数的进程如何交给正确的进程呢端口号正是解决这一问题的答案端口号(port)是传输层协议的内容。端口号用于标识一台计算机中唯一的进程。其本质是一个2字节16位的整数。一个端口号只能被一个进程占用但一个进程可以拥有多个端口号。只需确保一个端口号对应唯一一个进程即可。通过IP地址 端口号我们就可以得到全网唯一的一个进程于是我们惊讶的发现网络通信的本质其实就是进程间通信端口号范围划分0~1023知名端口号如HTTPFTPSSH等。这些广为使用的应用层协议它们的端口号都是固定的1024~65535这个范围才是我们能够使用的端口号。不过端口号是由OS动态分配的我们并不能指定端口号。理解端口号与PID我们讲端口号port是标识计算机中唯一的进程但是我们也知道PID也是标识计算机中唯一的进程。那为什么不使用PID而又引用端口号来标识呢因为如果我们用PID代替端口号那么操作系统与网络就会产生强耦合而我们追求的高内聚低耦合。所以就并没有这么做。理解源端口号与目的端口号传输成协议(TCP与UDP)的数据段中有两个端口号分别叫做源端口号与目标端口号。其本质就是在描述谁发送的数据、要发给谁。理解套接字socket我们上面讲到了IP 端口号标识了全网唯一的一个进程。我们把IP 端口号 就叫做套接字socket它能在整个网络里唯一确定一个进程方便网络中不同进程通过它来通信。简单认识传输层协议传输层是属于OS内核的用户想要调用任何接口使用任何功能都必须调用对应的系统调用。传输层协议有两个经典代表UDP、TCPTCP协议TCP(Transmission Control Protocol传输控制协议)其特点为有链接通信前需要先建立链接。比如打电话想要通信必须拨打电话并且等待对方接通。可靠传输确保数据准确有效到达。面向字节流以字节为单位传输数据。UDP协议UDP(User Dategram Protocol用户数据报协议)其特点为无链接通信前不想要建立链接。比如对讲机想要通信时直接发送信息即可不需要等待谁。不可靠传输不确保数据准确有效到达。面向数据报以独立数据为单位传输。网络字节序我们知道计算机是分为大端机与小端机的。 大端机意思是其内部的数据是按照大端存储的既高位数据放在低地址处低位数据放在高地址处。 小端机意思是其内部的数据是按照小端存储的既高位数据放在高地址处低位数据放在低地址处。发送数据的主机通常将缓冲区中的数据按照内存地址从低到高发送。但对于大小端主机来说就算是相同的数据发送后也会变得不一样所以为了保证大小端主机发送信息的一致性。TCP/IP协议规定网络数据流应采用大端字节序。不论大小端主机都必须遵守这个协议。大端主机发送数据不影响而小段主机发送数据时必须先将数据转化为大端再进行发送其中为了让代码在不同的大小端机器上具备可移植性我们可以使用以下库函数来完成网络字节序与主机字节序的转化代码语言javascriptAI代码解释#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标识hostn表示networkl表示32为长整数n表示16位短整数 如果主机是小端机这些函数就会进行相应的转化 如果主机是大端机这些函数将不会执行转化工作Socket编程接口代码语言javascriptAI代码解释// 创建 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);Sockadd结构体我们注意到在Socket接口中存在参数为struct sockeaddr* 的参数。下面我们来认识一下这个参数。套接字socket执行的通信标准是POSIX标准。而POSIX标准是包含了网络通信与本地通信的这也就意味着执行这套标准的socket要支持多种通信方式。但socket的实现者不想提供多个针对于不同通信的接口只想提供一个接口来完成多种通信方式。于是结构体Sockeaddr诞生了通过结构体的内存布局兼容实现struct sockaddr作为基类struct sockaddr_in与struct sockaddr_un作为子类继承基类、实现多态的作用。struct sockaddr_in用于网络通信struct sockaddr_un用于本地通信。这样就实现了一个接口多种通信功能。