第一部分:网络模型与协议栈
1. OSI 七层模型(开放系统互连模型)
这是一个理论参考模型,用于理解和设计网络体系结构。它定义了网络通信应该完成的七项主要任务,从上到下分层实现:
-
应用层:直接为用户提供网络服务接口。比如你使用浏览器访问网页、用邮件客户端发邮件,这些应用程序就在这一层工作。
-
表示层 :负责数据的格式转换。比如将数据加密/解密(SSL)、压缩/解压(gzip)、不同字符编码的转换(ASCII、UTF-8)。
-
会话层 :负责建立、管理和终止两个应用程序之间的会话连接。例如,它可以管理会话的恢复点,如果网络中断,可以从断点继续。
-
传输层 :提供端到端的可靠或不可靠的数据传输服务。主要协议是TCP和UDP。TCP像打电话,需要建立稳定连接,保证数据顺序和不丢失,适合文件传输。UDP像发短信,不保证对方一定收到,但速度快,适合视频、音频流。
-
网络层 :负责将数据包从源主机跨越多个网络路由到目标主机。主要协议是IP,它使用IP地址进行寻址。NAT(网络地址转换)也在这层工作。
-
数据链路层 :负责在同一网络内 (如一个局域网)两个直接相连的设备间传输数据。它将网络层传下来的数据包封装成"帧",加上目标/源MAC地址(物理地址),并进行差错校验。交换机工作在这一层。
-
物理层 :负责在物理媒介(如网线、光纤、无线电波)上传输原始比特流(0和1)。它定义电压大小、光信号频率、接口形状等。网卡、集线器工作在这一层。
重要理解:OSI是一个理想化的"教科书"模型,指出了网络应该做什么,但实际互联网采用的是更简洁的TCP/IP模型。
2. TCP/IP 四层模型(网际互联模型)
这是实际运行的互联网协议栈,将OSI的七层简化为四层:
-
应用层:合并了OSI的应用层、表示层、会话层的功能。HTTP、FTP、DNS、SMTP等协议都在这里。
-
传输层:和OSI传输层对应,提供TCP和UDP协议。
-
网络层:和OSI网络层对应,核心是IP协议。
-
网络接口层:合并了OSI的数据链路层和物理层,负责驱动网卡等硬件,处理与物理媒介的交互。
为什么叫协议栈? 因为数据在发送时,从上到下,每一层都会在数据前面加上自己的"头部"(就像套信封);接收时,从下到上,一层层拆开头部,读取信息。
第二部分:IP地址与网络基础
IP地址详解
-
表现形式 :IPv4地址是一个32位的二进制数,通常写成4个十进制数,用点分隔,如
192.168.1.1。 -
组成 :
IP地址 = 网络位 + 主机位。网络位标识一个网络,主机位标识该网络中的一台具体设备。 -
子网掩码 :用来区分IP地址中哪部分是网络位,哪部分是主机位。如
255.255.255.0表示前24位是网络位。
IP地址分类
| 类别 | 首字节范围 | 网络/主机位分配 | 用途 |
|---|---|---|---|
| A类 | 1 - 126 | 8位网络位,24位主机位 | 超大规模网络(如大型运营商) |
| B类 | 128 - 191 | 16位网络位,16位主机位 | 大中规模网络(如大学、大公司) |
| C类 | 192 - 223 | 24位网络位,8位主机位 | 中小规模网络(家庭、小公司) |
| D类 | 224 - 239 | 不区分网络/主机位 | 组播地址(一对多通信) |
| E类 | 240 - 255 | 保留 | 实验和研究用途 |
私有地址:只能在局域网内部使用,不能直接访问互联网。
-
A类:
10.0.0.0 ~ 10.255.255.255 -
B类:
172.16.0.0 ~ 172.31.255.255 -
C类:
192.168.0.0 ~ 192.168.255.255
特殊地址:
-
127.0.0.1:本机回环地址,用于测试本机网络服务。 -
192.168.0.255:广播地址,发送到这个地址的数据包,局域网内所有设备都会收到。
第三部分:Socket编程核心
1. Socket(套接字)
本质上是操作系统内核提供的一个文件描述符。但它不是指向硬盘上的一个文件,而是指向内核中的一个网络通信端点。通过这个端点,应用程序可以收发网络数据。
2. 网络通信五元组
一次完整的网络通信由五个要素唯一确定:
{源IP, 源端口, 目标IP, 目标端口, 传输层协议}。
-
IP地址:定位网络中的一台主机。
-
端口号:在一台主机上定位一个具体的应用程序(进程)。范围0-65535,其中0-1023为知名端口,如HTTP的80,SSH的22。
3. 字节序问题
计算机存储多字节数据(如一个整数)有两种顺序:
-
大端序 :高位字节存储在低地址。网络传输标准字节序。
-
小端序 :低位字节存储在低地址。x86/x64 CPU使用此序。
因此,在通过网络发送一个整型数据(如端口号)前,必须用 htons() 将其从主机字节序转换为网络字节序;接收时用 ntohs() 转换回来。
转换函数:
uint16_t htons(uint16_t hostshort); // 主机转网络(short)
uint16_t ntohs(uint16_t netshort); // 网络转主机(short)
uint32_t htonl(uint32_t hostlong); // 主机转网络(long)
uint32_t ntohl(uint32_t netlong); // 网络转主机(long)
// IP字符串与网络字节序转换
in_addr_t inet_addr(const char *cp); // "192.168.1.1" -> 网络序整数
char *inet_ntoa(struct in_addr in); // 网络序整数 -> "192.168.1.1"
第四部分:TCP编程流程详解
TCP服务器工作流程
-
socket():创建一个用于监听连接的套接字。
-
bind() :将这个套接字与一个具体的IP地址和端口号绑定。这样客户端才知道连接到哪里。
-
listen() :将这个套接字置于监听状态 ,并设置等待连接队列的长度(
backlog)。 -
accept() :阻塞等待,直到有客户端发起连接。当连接到来时,它返回一个新的套接字描述符,专门用于与这个客户端通信。
-
recv()/send() :使用
accept()返回的新套接字与客户端进行数据收发。 -
close():通信完毕,关闭套接字。
TCP客户端工作流程
-
socket():创建一个用于通信的套接字。
-
connect() :向服务器(指定的IP和端口)发起连接请求。此函数会触发TCP的三次握手。
-
send()/recv():连接建立后,进行数据收发。
-
close() :关闭连接,触发TCP的四次挥手。
关键理解:
-
listen()的套接字(监听套接字)只负责接受新连接,不进行实际数据传输。 -
accept()为每个客户端创建一个新的套接字(连接套接字),一对一通信。 -
setsockopt(listfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))这行代码很重要,它允许服务器程序重启后能立即重新绑定端口,而不用等待操作系统释放(TIME_WAIT状态)。
第五部分:TCP连接管理
三次握手(建立连接)
-
客户端 发送
SYN包(同步请求)到服务器,包含初始序列号seq=x。 -
服务器 收到后,回复
SYN-ACK包(同步确认),包含自己的初始序列号seq=y,以及对客户端SYN的确认ack=x+1。 -
客户端 收到服务器的
SYN-ACK后,再发送一个ACK包,确认服务器的SYN,ack=y+1。
代码体现 :客户端的 connect() 函数调用,会触发第一次 SYN 的发送。服务器的 listen() 使套接字准备好接受连接,accept() 在三次握手完成后返回。
四次挥手(断开连接)
假设客户端主动关闭:
-
客户端 发送
FIN包,表示自己数据发完了。 -
服务器 回复
ACK包,确认收到了FIN。此时进入半关闭状态:客户端不再发送数据,但还可以接收。 -
服务器 发送完自己的剩余数据后,发送
FIN包。 -
客户端 回复
ACK包。连接完全关闭。
代码体现 :任何一方调用 close() 都会触发这个过程。
第六部分:网络测试与调试工具
常用命令
-
ping:发送ICMP回显请求,测试网络连通性和延迟。 -
netstat:查看网络连接状态、路由表、接口统计。-
netstat -anp:查看所有连接及对应的进程。 -
netstat -n -t:只查看TCP连接。
-
-
telnet:基于TCP的远程登录工具,也可用于测试某个TCP端口是否开放(telnet IP 端口)。 -
arp:查看和操作ARP缓存表,将IP地址映射到MAC地址。
抓包分析
-
Wireshark :图形化抓包分析工具。可以设置过滤器(Filter)只抓取关心的流量,如
tcp.port == 9999或ip.addr == 192.168.1.100tcpdump -i eth0 -n port 80 # 抓取eth0网卡80端口的包
tcpdump -i lo -n host 192.168.0.130 # 抓取本地回环与指定IP的通信
如何排查网络问题:
-
用
ping测试基本连通性。 -
用
telnet IP 端口测试具体服务的TCP端口是否可达。 -
在服务端和客户端同时用
tcpdump或 Wireshark 抓包。 -
分析抓包文件:
-
检查TCP三次握手是否成功。
-
检查是否有数据包重传(可能是网络丢包或拥塞)。
-
检查应用层数据是否正确发送和接收。
-
第七部分:UDP编程
UDP与TCP有本质不同:
-
无连接 :不需要
connect()、listen()、accept()。 -
不可靠:不保证数据包一定到达,也不保证顺序。
-
面向数据报:每次发送都是一个完整的报文,有边界。
UDP服务器流程
-
socket(PF_INET, SOCK_DGRAM, 0):注意第二个参数是SOCK_DGRAM。 -
bind():必须绑定,因为客户端需要知道发送到哪个端口。 -
recvfrom():接收数据,同时可获得发送方的地址信息。 -
sendto():发送数据,需要指定目标地址。 -
close()。
UDP客户端流程
-
socket()。 -
(可选)
bind():如果不绑定,操作系统会随机分配一个端口。 -
sendto():发送数据,必须指定服务器地址。 -
recvfrom():接收数据。 -
close()。
关键函数:
// 发送,必须指定目标地址
sendto(sockfd, buf, len, 0, (struct sockaddr*)&dest_addr, addrlen);
// 接收,可获得来源地址
recvfrom(sockfd, buf, len, 0, (struct sockaddr*)&src_addr, &addrlen);