1. 网络编程基础回顾:TCP/IP协议栈、Socket编程基础、客户端-服务器模型

好,咱们正式开始。第一节课,我不会一上来就扔一堆晦涩的概念。

网络编程这东西,说白了就是让两台机器能“说上话”。你想想看,你在浏览器里输入一个网址,几毫秒后页面就出来了——这背后就是网络编程在干活。我当年刚入行时,总觉得这玩意儿很玄乎,直到亲手写了一个聊天程序,才真正理解“数据是怎么从A跑到B的”。

1.1 TCP/IP协议栈:数据是怎么“打包”的?

先聊协议栈。很多人一听到“七层模型”、“四层模型”就头大。其实你不用背,记住一个核心就行:每一层只关心自己的事,下层为上层服务。

我习惯把TCP/IP协议栈简化成四层来看:

层级 典型协议 核心职责 我眼中的比喻
应用层 HTTP、FTP、WebSocket 定义数据格式和业务逻辑 写信的内容
传输层 TCP、UDP 端到端可靠传输 / 端口寻址 信封上的收件人
网络层 IP 路由选择、IP寻址 信封上的地址
网络接口层 以太网、Wi-Fi 物理介质上的比特流传输 邮递员的交通工具

嗯,这里要注意:TCP和UDP的区别是面试高频题,也是你后面理解粘包拆包的基础。

  • TCP:面向连接、可靠、有序、有流量控制。代价是慢一点,有额外开销。
  • UDP:无连接、不可靠、无序、无流量控制。优点是快,适合实时场景。

核心记忆点:TCP保证你发的数据“一定到、不乱序、不重复”;UDP只管“扔出去,爱到不到”。

我在项目中遇到过一个问题:用UDP传文件,结果丢包率高达30%,文件永远打不开。后来换成TCP,虽然慢了点,但至少能用了。所以选协议时,一定要想清楚你的场景是“要快”还是“要稳”。

1.2 Socket编程基础:你写的代码怎么跟网卡打交道?

Socket,中文叫“套接字”。名字挺拗口,其实它就是操作系统提供的一个编程接口。你通过它告诉内核:“我要发数据到某个IP的某个端口”。

我个人习惯把Socket理解成一个“管道”。你往管道里写数据,内核帮你封装成TCP段、IP包,最后扔到网线上。对面从管道里读数据,一层层解包,拿到原始内容。

Python里用socket模块,代码非常简洁。我带你走一遍最核心的流程:

服务端(Server)

import socket

# 1. 创建Socket对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. 绑定IP和端口
server.bind(('0.0.0.0', 8888))

# 3. 开始监听
server.listen(5)
print('服务端已启动,等待连接...')

# 4. 接受客户端连接
conn, addr = server.accept()
print(f'客户端 {addr} 已连接')

# 5. 接收数据
data = conn.recv(1024)
print(f'收到: {data.decode()}')

# 6. 发送响应
conn.send(b'Hello, Client!')

# 7. 关闭连接
conn.close()
server.close()

客户端(Client)

import socket

# 1. 创建Socket对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. 连接服务端
client.connect(('127.0.0.1', 8888))

# 3. 发送数据
client.send(b'Hello, Server!')

# 4. 接收响应
data = client.recv(1024)
print(f'收到: {data.decode()}')

# 5. 关闭连接
client.close()

避坑指南:我曾经在服务端忘记调用 bind(),结果程序一直报错“Address already in use”。后来养成习惯,每次启动前先检查端口是否被占用。另外,recv(1024) 里的1024是缓冲区大小,不是一次只能收1024字节——这个后面讲粘包时会细说。

1.3 客户端-服务器模型:谁主动,谁被动?

这个模型其实很简单:客户端主动发起请求,服务端被动等待并处理请求。

你想想看,你每次打开,客户端就去连腾讯的服务器。服务器不会主动连你的手机——因为你的IP是动态的,服务器根本不知道你在哪。这就是典型的C/S模型。

我画了一张图,帮你理清整个流程:

客户端-服务器模型核心流程 客户端 1. 创建Socket 2. connect() 发起连接 3. send() 发送请求 4. recv() 接收响应 5. close() 关闭连接 服务端 1. 创建Socket 2. bind() 绑定端口 3. listen() 开始监听 4. accept() 接受连接 5. recv() / send() 处理 TCP三次握手 请求数据 响应数据 关键点:服务端永远是被动等待,客户端永远是主动发起

这个图里,我特意把三次握手和请求/响应分开了。你注意看:connect() 触发三次握手,握手完成后,send() 和 recv() 才开始传真正的业务数据。

曾经踩过的坑:我在一个项目里,客户端连续调了两次 send(),服务端只调了一次 recv(),结果两次发送的数据被合并成一次收到了。这就是典型的粘包——也是咱们这门课的核心主题。别急,后面几章我会手把手带你解决它。

1.4 本章小结:你该带走什么?

好,第一节课的内容就这些。我不喜欢堆砌概念,你只要记住三件事:

  1. TCP/IP协议栈是分层协作的,每层只干自己的活。
  2. Socket是操作系统给你的“管道”,通过它跟网络打交道。
  3. C/S模型的核心是“客户端主动,服务端被动”。