1 基础概念
1.1 HOST和PORT
Claude-2-100k:
我来用简单的语言解释一下HOST和PORT这两个网络术语:
HOST就是设备的IP地址,比如"127.0.0.1"就是我们本机的IP地址;
PORT就是端口号,可以把它简单理解为设备上的门牌号。
举个类比,我们要给某个人发信件,需要知道两个信息:
- 收信人的地址(HOST),比如北京市XX区XX街1号;
- 收信人邮箱的编号(PORT),比如101室(门牌号为101)。
那么我们写信的地址就是:
北京市XX区XX街1号(HOST)101室(PORT)
同理,在网络上传输数据的时候,也需要指定对方的IP地址和端口号,才能正确送达。
1.2 Socket
Claude-2-100k
我来用通俗的语言解释一下什么是socket:
socket就像是不同程序之间的一个管道或者通道。想象一下我们要连接两个房间,需要在墙上打一个洞,两个房间之间就可以通过这个洞互通了。
在网络通信中,两个程序也可以通过socket这个“洞”来互相通信。一个程序创建一个socket,就像打洞一样,其他程序可以连接到这个socket,两者就可以互相发送和接收数据了。
具体到代码中,socket通常有以下使用步骤:
- 调用socket()函数创建一个socket“洞”。
- 使用connect()函数连接到对方的地址和端口。
- 通过send()和recv()在两个socket间发送接收数据。
- 调用close()关闭socket。
所以socket其实就像是一个网络数据的双向通道,程序只需要连接到一个socket,就可以互相通信了。
南溪:哈哈,我怎么感觉socket有点像“虫洞”。
Claude-2-100k:
没错,把socket比喻成一个连接两个程序的“虫洞”也是非常贴切的!
虫洞可以连接两个不同的空间或时间,socket也可以连接运行在不同系统、不同设备上的程序。
通过 socket 这个“虫洞”,数据可以直接传输到另一个程序,不需要关心设备之间的距离或细节。
这就像两个宇宙通过虫洞实现快速通行一样,非常神奇。
用虫洞这个比喻来理解 socket 通信机制,直观又形象。它抽象出了网络通信最核心的概念,即两个程序只要连接到同一个 socket,就可以像通过一个虫洞一样实现直接的数据传输。
所以,把 socket 比作连接网络程序的虫洞,是一个非常恰当且生动的比喻。这种类比方法可以让我们从一个新的视角理解计算机网络的一些概念,非常适合初学者。
你的这个联想很棒!用这种简单有趣的方式理解复杂的专业知识,是个非常聪明的学习方法。
1.3 TCP: Transmission Control Protocol, 传输控制协议
南溪:我觉得如果准确一点来说,TCP就像打电话,UDP就像在用对讲机讲话;
Claude-2-100k:
你的对比非常准确!
的确,把UDP比作对讲机讲话,而不是发短信,可以更清楚地反映出UDP的实时性:
- 对讲机可以直接讲话,不需要像电话一样先建立连接。这就像UDP可以直接发送数据包,无需建立连接。
- 对讲机讲话时对方不一定能立即回复,信息可能会丢失或顺序混乱,这就像UDP不保证可靠传输和顺序。
- 但是对讲机可以快速讲话,具有实时性,这正是UDP的优势。
而TCP像打电话,需要先连接,但是可以持续双向对话,信息顺序可靠,不会丢失数据。
通过把UDP比作对讲机,TCP比作电话,可以非常明确地反映两者之间的区别 - UDP用于实时但不可靠的通信,TCP用于高可靠的顺序传输。
您通过更贴近实际的对比提高了比喻的准确性,使抽象概念具体化,能让人更准确地理解TCP和UDP,非常有启发意义。
2 Python cookbook
2.1 获取空闲端口
import socket
def find_free_port():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("", 0)) # 绑定到系统分配的端口
return s.getsockname()[1] # 返回分配的端口号
free_port = find_free_port()
print(f"找到的可用端口号是: {free_port}")
Q:那么“增加socket.get_free_port()
函数”是否合适呢?
之前,我们考虑为了更加方便地获得空闲端口,于是在【python/cpython】上提出了建议希望“增加socket.get_free_port()
函数”(issue#114927);
不过,在阅读了老师们的回复之后,我们发现这样的做法是不太合适的,需要考虑的情况如下:
(1)无法保证完全避免竞争
ChatGPT:
在Windows系统中,确实可以为当前程序申请特定端口以避免潜在的竞争条件。这通常通过在创建套接字时绑定到特定端口来实现。一旦端口被绑定,其他程序就不能使用该端口,从而减少了端口冲突的可能性。但是,需要注意的是,这种方法并不保证完全避免竞争条件,因为在检查端口是否可用和实际绑定端口之间仍然存在时间差。为了确保端口的独占性,应用程序需要妥善处理错误并做好异常处理。
(2)"port"并非是所有网络协议的通用语义
Eric V. Smith 在issues中说:
Not all protocols have the concept of a port. TCP and UDP do, ICMP does not. And the TCP “namespace” for used/free ports is different from UDP’s.
2.2 Socket连接
import socket
# 创建socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置timeout
sock.settimeout(2)
# 连接服务器
s.connect(('localhost', 10000))
# 发送数据 || 接收数据
# ...
# 关闭连接
print('完毕,关闭连接')
s.close()
2.2.1 设置超时:socket.settimeout
[Python Doc]:
Theconnect()
operation is also subject to the timeout setting, and in general it is recommended to call settimeout() before calling connect() or pass a timeout parameter to create_connection().
2.3 Data.decode():解码数据
Claude-instant-100k:
对应的解释如下:
- socket接收到的数据数据是bytes类型的二进制数据。
- 若直接print二进制数据,显示效果不是很友好;
- 所以需要解码为字符串,Python中bytes对象的
decode()
方法可以用来进行解码;- bytearray对象也有对应的
decode()
方法,用法和bytes对象是一致的;- 默认解码为UTF-8字符集。
decode()
方法将bytes解码为字符串。- 打印字符串的时候显示效果就是可读的文本。