3、TCP服务端开发:socket创建、bind绑定、listen监听、accept接受连接、收发数据
好,咱们今天来聊聊TCP服务端开发。说实话,这是网络编程里最基础、也最核心的一块。你想想看,几乎所有互联网应用——从Web服务器到聊天软件——底层都是这套流程。
我个人习惯把TCP服务端开发总结为五个步骤:创建socket、绑定地址、监听端口、接受连接、收发数据。这五步走完,一个能用的服务端就搭起来了。
核心流程速览
socket() → bind() → listen() → accept() → recv()/send()
每一步都有它的坑,咱们一个一个来拆。
3.1 创建socket——一切的开端
第一步,创建socket。说白了就是跟操作系统说:「嘿,给我一个网络通信的句柄。」
在Python里,一行代码搞定:
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
这里有两个参数:AF_INET 表示IPv4地址族,SOCK_STREAM 表示TCP协议。我记得刚学的时候老搞混,后来记住一句话:「AF_INET是地址类型,SOCK_STREAM是传输类型」,就再也没错过。
小提示:如果你要支持IPv6,把AF_INET换成AF_INET6就行。不过目前大部分场景还是IPv4为主。
3.2 bind绑定——给socket一个身份
socket创建好了,它还是个「无名氏」。你得给它绑定一个IP地址和端口号,这样客户端才知道往哪连。
server_address = ('0.0.0.0', 8888)
server_socket.bind(server_address)
这里'0.0.0.0'表示监听所有网络接口。如果你只想让本机访问,可以改成'127.0.0.1'。端口号我习惯用8888,当然你也可以用其他未被占用的端口。
注意:端口号范围是0~65535,但0~1023是系统保留端口,需要管理员权限才能用。我曾经在项目里图省事用了80端口,结果部署时发现没权限,折腾了半天才换成8080。嗯,这个坑我替你们踩过了。
3.3 listen监听——准备好接客
绑定完地址,socket就进入「待机状态」。但你还得告诉它:开始监听吧!
server_socket.listen(5)
print("服务器已启动,等待客户端连接...")
参数5是backlog,表示最大等待连接数。说白了就是:当服务器忙不过来时,最多允许5个客户端在门口排队。超过这个数,新连接会被拒绝。
我个人建议backlog设5~10就够了。设太大反而浪费资源,因为操作系统有上限。
3.4 accept接受连接——终于等到你
这一步是阻塞的。程序会停在这里,直到有客户端连上来。
client_socket, client_address = server_socket.accept()
print(f"收到来自 {client_address} 的连接")
accept()返回两个东西:一个是新的socket对象(用于和这个客户端通信),另一个是客户端的地址信息。注意,这里返回的client_socket和原来的server_socket不是同一个东西——server_socket继续监听,client_socket专门服务这个客户端。
关键点:accept()会阻塞当前线程。如果你希望同时服务多个客户端,就得用多线程或异步IO。这个后面会讲。
3.5 收发数据——真正的业务逻辑
连接建立后,就可以和客户端聊天了。收数据用recv(),发数据用send()。
# 接收数据
data = client_socket.recv(1024)
print(f"收到消息: {data.decode('utf-8')}")
# 发送数据
response = "消息已收到!"
client_socket.send(response.encode('utf-8'))
recv(1024)里的1024是缓冲区大小,单位是字节。如果消息超过1024字节,需要多次接收。我建议根据实际业务调整这个值——传小文本1024够用,传文件就得设大点。
注意:recv()返回空字节串(b'')时,表示客户端已经断开连接。一定要处理这个情况,否则程序会卡死。我曾经在早期项目里没处理这个,结果客户端一断线,服务端就死循环了……
3.6 完整示例——一个能跑的服务端
把上面五步串起来,就是一个完整的TCP服务端:
import socket
def start_server():
# 1. 创建socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定地址
server_address = ('0.0.0.0', 8888)
server_socket.bind(server_address)
# 3. 开始监听
server_socket.listen(5)
print(f"服务器启动,监听 {server_address}")
while True:
# 4. 接受连接
client_socket, client_address = server_socket.accept()
print(f"新连接: {client_address}")
try:
# 5. 收发数据
data = client_socket.recv(1024)
if data:
print(f"收到: {data.decode('utf-8')}")
client_socket.send("你好,客户端!".encode('utf-8'))
finally:
client_socket.close()
if __name__ == '__main__':
start_server()
这个例子虽然简单,但五脏俱全。你把它跑起来,然后用telnet或自己写个客户端连上去试试。
3.7 避坑指南——我踩过的那些雷
做TCP服务端开发,有几个坑我几乎每次都会遇到:
- 端口被占用:服务端退出后,端口可能还处于TIME_WAIT状态。加个
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)就能解决。 - 粘包问题:TCP是流式协议,没有消息边界。我习惯在消息前加4字节的长度头,这样接收方就知道该读多少数据。
- 异常处理:网络随时可能断开,所有recv/send都要包在try里。别偷懒,否则线上出问题你都不知道咋回事。
- 资源泄漏:用完的socket记得close。我见过有人开了几万个连接不关,直接把服务器搞崩了。
我的习惯:每次写服务端,我都会先画个流程图,把异常路径标出来。这样写代码时心里有数,不容易漏掉边界情况。
3.8 小结
TCP服务端开发,说白了就是五步走:创建、绑定、监听、接受、收发。每一步都有它的讲究,但核心逻辑并不复杂。你只要记住:socket是门,bind是门牌号,listen是开门营业,accept是接待客人,recv/send是聊天。
把这个流程练熟了,后面学多线程、异步IO、高并发就轻松多了。嗯,今天就到这,动手试试吧。