第一章:网络编程基础回顾
大家好,我是你们的老朋友。做网络编程优化这么多年,我见过太多人一上来就追求高大上的框架,结果连最基础的Socket模型都搞不清楚。说实话,这就像盖楼不打地基——早晚要塌。今天咱们就把这块地基夯实了。
1.1 Socket编程模型:从零开始
Socket,中文叫套接字,说白了就是操作系统提供的一个通信接口。你想想看,两台机器要聊天,总得有个门吧?Socket就是这个门。
我个人习惯把Socket编程分成三步走:
- 创建Socket——开门
- 绑定/连接——确定跟谁聊
- 收发数据——开始聊天
来看一个最简单的TCP服务端代码:
import socket
# 创建Socket
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_sock.bind(('0.0.0.0', 8888))
# 开始监听
server_sock.listen(5)
print('服务端已启动,等待连接...')
while True:
# 接受客户端连接
client_sock, addr = server_sock.accept()
print(f'收到来自 {addr} 的连接')
# 接收数据
data = client_sock.recv(1024)
print(f'收到数据: {data.decode()}')
# 发送响应
client_sock.send(b'Hello, Client!')
# 关闭连接
client_sock.close()
这段代码看着简单,但我得提醒你——它只能同时处理一个客户端。为什么?因为accept()和recv()都是阻塞的。我在项目中遇到过,有人拿这个代码去生产环境,结果第二个用户一来就卡死了。嗯,这里要注意。
1.2 TCP vs UDP:选谁更合适?
很多新手问我:「TCP和UDP到底选哪个?」我的回答是:看场景。
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(先握手) | 无连接(直接发) |
| 可靠性 | 可靠(有重传机制) | 不可靠(丢了就丢了) |
| 数据顺序 | 保证顺序 | 不保证顺序 |
| 传输速度 | 相对慢 | 快 |
| 典型应用 | HTTP、FTP、邮件 | 视频直播、DNS、游戏 |
我曾经帮一个直播平台做优化,他们一开始用的TCP,结果卡顿严重。我建议换成UDP,配合应用层的丢包补偿,延迟直接降了60%。说白了,TCP的可靠性是用延迟换来的,如果你能接受偶尔丢包,UDP就是更好的选择。
核心原则:需要数据完整性的用TCP,追求实时性的用UDP。
1.3 阻塞与非阻塞IO:别再傻等了
阻塞IO,就是程序在那干等着。比如你调用recv(),如果对方没发数据,你的程序就卡在那了。非阻塞IO呢?它会立刻返回一个错误,告诉你「没数据,你先干别的去」。
来看对比代码:
import socket
# 阻塞模式(默认)
sock = socket.socket()
sock.setblocking(True) # 这行不写也行,默认就是阻塞
data = sock.recv(1024) # 没数据?等着!
# 非阻塞模式
sock = socket.socket()
sock.setblocking(False)
try:
data = sock.recv(1024) # 没数据?立刻抛异常
except BlockingIOError:
print('没数据,我先干点别的')
你想想看,如果服务端用阻塞模式,一个recv()就把线程卡死了。那怎么处理多个客户端?传统做法是来一个客户端就开一个线程。但线程多了,上下文切换的开销也大。
我的建议:小项目用多线程阻塞IO就够了,简单好维护。高并发场景,用非阻塞IO配合事件循环(比如select、epoll),这才是高性能的基石。
我曾经接手过一个老项目,每个连接开一个线程,同时在线5000人时服务器直接崩了。改成非阻塞IO + epoll后,同样的机器扛到了5万连接。嗯,差距就是这么大。
避坑指南:非阻塞IO的recv()会频繁返回EAGAIN错误,如果你在循环里不停地调用它,CPU会被吃满。一定要配合select/poll/epoll来用,让操作系统告诉你「什么时候数据准备好了」。
知识体系总览
下面这张图,是我自己总结的网络编程基础知识框架。你看一眼,心里就有谱了。
好了,这一章的内容就这些。Socket模型是骨架,TCP/UDP是血肉,阻塞与非阻塞是灵魂。这三样搞明白了,后面的性能优化才有得谈。
可可精品资料库