一条泥憨鱼头像
关注
【从0开始学习计算机网络】| TCP Socket 的 bind、listen、accept、connect封面图

【从0开始学习计算机网络】| TCP Socket 的 bind、listen、accept、connect

🌈个人主页:一条泥憨鱼(欢迎各位大佬莅临)

❄️《数据结构》 ❄️《AI与Agent那些事》

❄️《从0开始学计算机网络》 ❄️《后端开发》

前言:

这篇文章整理了 TCP Socket 建立连接的完整流程,从服务端的 bind、listen 到客户端的 connect,再到服务端的 accept。这些动作串起来就是一次网络通信从"准备"到"接通"的全过程。

先搞懂 Socket 是什么——一扇"门"

假设你想开一家客服热线。要能接到客户的电话,你得先做几件事:装一部电话机、申请一个对外公布的号码、把线路开通、然后坐在那儿等电话响。

TCP 网络编程里的服务端,做的事情几乎一样。只不过它的"电话机"叫 Socket。

Socket 这个词直译是"插座",也有人叫它"套接字"。你可以把它理解成程序和网络之间的一扇门:程序在门里面,网络在外面。程序想把数据发出去,就从这扇门递出去;网络有数据进来,也从这扇门送进来。没有这扇门,程序根本碰不到网络。

那数据要送到哪台机器、哪个程序呢?靠两个东西定位:

- IP 地址:相当于门牌号,标识网络上的哪一台机器。比如 `192.168.1.10`。

- 端口:相当于房间号,标识这台机器上的哪一个程序。比如 `8080`。

一台机器上可以同时跑很多个网络程序——浏览器、微信、你的服务端代码。它们共用同一个 IP,但各自占着不同的端口。数据包到了这台机器,操作系统看端口号,就知道该把数据交给哪个程序。所以端口是区分不同程序的关键。

还有一点要分清角色:

- 服务端 Socket:被动等待,别人来找我。

- 客户端 Socket:主动出击,我去找别人。

下面所有的函数,都是围绕这两种角色展开的。

服务端的准备动作——bind 和 listen

服务端要能接客,得先做两步准备。

bind:给热线申请一个固定号码

创建好一个 Socket 之后,它还是个"没号码的电话机"。`bind` 的作用就是把这个 Socket 绑定到一个具体的 IP 和端口上。

为什么要固定端口?因为客户端得知道往哪儿打。你的服务对外说"我在 8080 端口",那所有客户端都会往这个端口发连接。如果端口每次都变,客户端根本找不到你。

import socket

# 创建一个 TCP Socket

# AF_INET 表示用 IPv4 地址,SOCK_STREAM 表示用 TCP 协议

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)



# 绑定到本机所有网卡的 8080 端口

# '0.0.0.0' 表示监听本机所有 IP,不限定某一块网卡

# 8080 是我们选定的端口号

server_socket.bind(('0.0.0.0', 8080))

`bind` 最常见的失败原因是端口被占用。报错一般长这样:`Address already in use`。意思是这个端口已经有别的程序在用了。

这里有坑:程序刚退出,端口可能还处于 `TIME_WAIT` 状态(TCP 正常关闭连接后会保留一段时间),短时间内重新 bind 会失败。开发时可以在 bind 之前加一句设置:

# 允许端口在 TIME_WAIT 状态下被重用,方便开发调试时快速重启

server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

端口号的范围是 0 到 65535。其中 1024 以下一般是系统保留给已知服务的(比如 80 是 HTTP、443 是 HTTPS),普通程序要用得用 1024 以上。

listen:告诉操作系统"我开始接客了"

bind 只是给了号码,电话线路还没开通。`listen` 才是把这个 Socket 切换成"监听状态",相当于告诉操作系统:从现在起,有客户端来连这个端口,你就接待,别拒绝。

# 开始监听,backlog 设为 5

server_socket.listen(5)

这里的 `backlog` 参数值得说一下。它表示等待被处理的连接队列的最大长度。

用排队叫号来理解:客服热线开通后,同一时间可能有很多人打进来。操作系统会先把这些连接放进队列里排着,你的程序一个个按顺序处理。这个队列的长度就是 backlog。

严格来说,TCP 的连接队列分两个:

- 半连接队列:收到了客户端的连接请求,但握手还没完成,连接还没真正建立。

- 全连接队列:握手已经完成,连接建好了,就等你的程序调用 `accept` 把它取走。

backlog 主要影响的是全连接队列的长度。如果队列满了,新的连接请求可能会被丢弃或拒绝。这也是为什么高并发服务要合理设置 backlog,还要尽快 accept 把连接取走。

客户端发起连接——connect 与三次握手

服务端准备好了,客户端这边就一个动作:连接。

connect:拨号

客户端创建 Socket 后,调用 `connect` 主动发起连接,指定要连的服务端 IP 和端口。

import socket

# 创建 TCP Socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)



# 连接服务端:IP 是 127.0.0.1,端口是 8080

# 127.0.0.1 是本机回环地址,用来在本机测试

client_socket.connect(('127.0.0.1', 8080))

这一步就相当于"拨号"。但拨号不是拨出去就完事了,中间要经过 TCP 三次握手,确认双方都能收发数据。

connect 默认是阻塞的。也就是说,调用 connect 之后,程序会停在这一行,一直等到三次握手完成、连接真正建立,才会继续往下走。如果服务端没开、或者网络不通,connect 会卡住直到超时然后报错。

服务端接收连接——accept 返回的是"新电话"

现在客户端连上来了,服务端得把它"接起来"。这个动作就是 `accept`。

accept 返回一个全新的 Socket

accept 返回的不是原来那个监听 Socket,而是一个全新的 Socket,专门用来和这个客户端通信。

# accept 会阻塞,直到有客户端连接进来

# 返回值:

#   conn —— 全新的 Socket,专门和这个客户端通信

#   addr —— 客户端的地址信息(IP 和端口)

conn, addr = server_socket.accept()



print('新连接来自:', addr)



# 用 conn 收发数据,而不是 server_socket

data = conn.recv(1024)

conn.send(b'hello client')



# 通信结束,关闭的是这个连接 Socket

conn.close()

```

用前台接线员来理解:监听 Socket(server_socket)就是那个接线员,它只负责转接,不负责聊天。有客户打进来,接线员把线路转给一个客服,然后继续守着电话等下一个。真正和客户聊天的是那个客服,也就是 accept 返回的新 Socket。

- `server_socket` 只用来 `accept`,它一直守着 8080 端口。

- `conn` 用来收发数据,连接关掉它就行,监听 Socket 不受影响。

为什么要循环 accept

因为接线员接待完一个客户,还得接下一个。服务端通常写成循环:

while True:

    # 每来一个新连接,就取出一个新的 Socket

    conn, addr = server_socket.accept()

    # 处理这个客户端的请求

    handle_client(conn)

但这样写有个问题:`handle_client` 如果处理得慢,后面的客户端就得一直等着,接线员被占住了。这是单线程 accept 的局限。

要同时服务多个客户端,常见的方向有:每个连接开一个线程或进程,或者用 IO 多路复用(比如 `select`、`epoll`)在一个线程里管理大量连接

收束

装电话(创建 Socket)、申请号码(bind)、开通线路(listen)、守着等来电(accept),客户那边拨号(connect)。整套动作走完,一条 TCP 连接才算真正建立。

一个可迁移的判断方法:看任何一段 TCP 服务端代码,先找"监听 Socket"和"连接 Socket"是不是两个不同的变量。如果只有一个 Socket 变量贯穿始终,或者把 accept 的返回值又赋值回监听 Socket,那这段代码八成有问题。

两个易错:一是忘了循环 accept,服务端接完一个连接就退出了;二是把监听 Socket 关掉,导致后续再也接不到新连接——该关的永远是 accept 返回的那个连接 Socket。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2503_94545876/article/details/167398929

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--