socket 是一种经常使用的进程间通讯接口。服务器
Python 实现 socket 的主体对象是 socket.socket
类。两个互相链接的 socket 对象之间是对等的,所以他们的链接过程基本上是这样的:框架
用代码来解释就是这样:socket
乙:code
import socket sock = socket.socket() sock.bind(('127.0.0.1', 2333)) sock.listen(1) conn, addr = sock.accept() # 这里可能会阻塞 data = conn.recv(1024) # 同上
甲:server
import socket sock = socket.socket() sock.connect(('127.0.0.1', 2333)) sock.send(b'hello')
其中乙方的 .bind
方法就是先声明并占用一个通讯端口,这样甲方才知道第一次的链接请求应该发送到哪。而后乙方调用 .listen
方法开始监听,当甲方的 connect
请求发送过来后,调用 accept
方法便可接受甲方的链接请求,此方法返回一个新的 socket 对象(conn
)和甲方的地址。对象
因此这里 type(conn) is type(sock)
是会返回 True
的,即当你调用一个已绑定的 socket 对象的 listen 方法时,他实际会调起一个服务器监听绑定的地址,并返回一个 新的 socket 对象用于通讯,而后原 socket 对象继续监听,并在 accept 时继续建立新的 socket 对象。这里的甲乙 sock
对象是一种 C/S 模型。刚才说的对等模型实际上是指 甲方的 sock
对象和乙方的 conn
对象。接口
所以这里的一个问题在于,乙方的 Server 在面对多个甲方的随机 connect 请求时,极可能没办法作到当即接受(.accept
),而又不可能让甲方一直等待。因此 listen
方法提供了一个整型的 backlog
参数,在上例中其为 1. 这个参数表示在 accept 以前最多能够积压多少个 connect 请求。实际上说是积压,实际上是直接返回了确认接受的信息。即,若是上面代码中乙方先不调用 accept,甲方也是能够 send 的,b'hello' 会被缓冲起来。乙方再调用 accept 和 recv 依然能够拿到数据。而由于上面设置的 backlog 是 1,因此若是在乙方 accept 以前又有一个 socket 向乙方发起 connect 请求,他就会当即收到一个显式的(积极的)拒绝链接响应。进程
socket 由于是语言无关的接口,因此只能用它发送字节码(二进制)。因此你须要 encode/decode
一下你的字符串对象。而数据流(socket.SOCK_STREAM
)又没有既定的边界。因此须要使用者自行管理边界问题。即甲方前后发送的两段数据,在乙方的缓冲中虽然有前后顺序,却没有间隔。ip
socketserver 是 Python 标准库的一个包,功能如其名,是一个 socket 服务器开发框架。服务器开发