1. Socket — 底层网络接口
Socket(套接字)底层网络接口: socket — Low-level networking interface
1.1 建立连接
Socket 构造方法:
# 创建一个 socket 对象
#
# family: 套接字地址族, 默认为 socket.AF_INET,
# 还有 AF_INET6, AF_UNIX, AF_CAN, AF_PACKET, AF_RDS
#
# type: 套接字类型, 主要为 SOCK_STREAM 和 SOCK_DGRAM(还有其他),
# 分别表示 面向连接(TCP) 和 非连接(UDP)。
#
# proto: 套接字协议, 默认为 0
#
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
服务端(TCP/UDP) 方法:
# 将 socket 绑定到指定地址, 在 AF_INET 下,
# address 以元祖 (host, port) 的形式表示,
# host 可以为空字符串 '' 表示所有可用接口
socket.bind(address)
服务端(TCP) 方法:
# 开始 TCP 连接监听,
#
# backlog: 允许的最大连接数,
# 如果指定, 则必须 >= 0(如果较低, 可以设置为 0),
# 如果不指定, 则自动选择默认的合理值
socket.listen([backlog])
# 接收一个 TCP 客户端连接(阻塞式等待客户端连接)
#
# 返回一个元祖 (socket object, address info)
#
# socket object: 与客户端建立的 socket,
# address info: 一个元祖 (host, port) 表示客户端的地址信息
#
socket.accept()
客户端(TCP) 方法:
# 连接 TCP 服务器, address 为元祖 (host, port), 如果出错(如超时、找不到主机等), 将抛出异常。
socket.connect(address)
# 连接 TCP 服务器, 出错时不抛出异常, 而是返回错误码, 返回 0 表示连接成功。
socket.connect_ex(address)
1.2 数据发送/接收
发送/接收(TCP) 方法:
# 发送 TCP 字节数据, 该 socket 必须已连接到远程 socket。
#
# bytes: 需要发送的字节数据
# flags: 可选参数, 默认为0
#
# 返回已发送的字节数(数据有可能没有全部发送)。
socket.send(bytes[, flags])
# 发送 TCP 字节数据, 该 socket 必须已连接到远程 socket,
# 与 send() 不同的是该方法会持续发送数据, 直到所有的数据都已发送或发生错误为止。
#
# bytes: 需要发送的字节数据
#
# 返回 None 表示发送成功
socket.sendall(bytes[, flags])
# 发送文件数据到 TCP 连接, 该 socket 必须已连接到远程 socket。
#
# file: 文件对象, 必须是以二进制模式打开的常规文件
# offset: 从文件的哪里开始读取
# count: 要发送的字节总数, None 表示发送文件直到达到 EOF
#
# 返回已发送的字节总数
socket.sendfile(file, offset=0, count=None)
# 接收 TCP 字节数据
#
# bufsize: 一次允许接收的最大数据量
#
# 返回 接收到的 bytes
socket.recv(bufsize[, flags])
# 接收 TCP 字节数据, 把数据存储到缓冲区 buffer 中(而不是创建一个 bytes)。
#
# buffer: 可写的字节缓冲区, 例如 bytearray
# nbytes: 最大接收的字节数, 不传(或传0), 则为缓冲区的可用大小
#
# 返回接收到的字节数
socket.recv_into(buffer[, nbytes[, flags]])
发送/接收(UDP) 方法:
# 发送 UDP 字节数据, 该 socket 不应连接到远程 socket。
#
# bytes: 需要发送的字节数据
# address: 发送到的目标地址, 以元祖 (host, port) 来表示
#
# 返回发送的字节数
socket.sendto(bytes, address)
socket.sendto(bytes, flags, address)
# 接收 UDP 字节数据
#
# bufsize: 一次允许接收的最大数据量
#
# 返回 元祖 (bytes, address),
# bytes 表示接收到的字节数据,
# address 也是一个元祖(host, port), 表示客户端地址(回传数据时使用)
socket.recvfrom(bufsize[, flags])
# 接收 UDP 字节数据, 把数据存储到缓冲区 buffer 中(而不是创建一个 bytes)。
#
# buffer: 可写的字节缓冲区, 例如 bytearray
# nbytes: 最大接收的字节数, 不传(或传0), 则为缓冲区的可用大小
#
# 返回 元祖 (nbytes, address),
# nbytes 表示接收到的字节数,
# address 也是一个元祖(host, port), 表示客户端地址(回传数据时使用)
socket.recvfrom_into(buffer[, nbytes[, flags]])
1.3 其他方法
其他(TCP/UDP) 常用方法/属性:
# (TCP) 返回 socket 连接到的 远程地址, 返回元祖 (host, port)
socket.getpeername()
# 返回 socket 自己本地的地址, 返回元祖 (host, port)
socket.getsockname()
# 设置 socket 阻塞操作的超时时间, value 值为浮点类型的 秒数,
# 0 表示非阻塞模式, None 表示阻塞模式(没有超时期限)
socket.settimeout(value: float)
# 返回当前设置的 socket 阻塞操作的超时时间
socket.gettimeout()
# 设置 socket 的 阻塞/非阻塞 模式, True 表示阻塞(默认), Falst 表示非阻塞。
#
# sock.setblocking(True) 相当于 sock.settimeout(None)
# sock.setblocking(False) 相当于 sock.settimeout(0.0)
#
socket.setblocking(flag: bool)
# 返回 socket 当前的 阻塞/非阻塞 模式, Falst 表示非阻塞, True 表示阻塞。
socket.getblocking()
# (TCP) 比较优雅的关闭连接, 单方向或双方向关闭连接。
# how:
# socket.SHUT_RD 不允许进一步接收
# socket.SHUT_WR 不允许进一步发送
# socket.SHUT_RDWR 不允许进一步发送和接收
socket.shutdown(how)
# (TCP/UDP) 关闭 socket 连接。
# close() 释放与连接关联的资源, 但不一定立即关闭连接。
# 如果想及时关闭连接, close() 之前调用 shutdown()。
socket.close()
socket.family # 套接字地址族
socket.type # 套接字类型
socket.proto # 套接字协议
2. UDP 示例
UDP 服务端:
import socket
# 创建面向非连接(UDP)服务端 Socket
ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定 主机和端口, "" 表示本地所有可用接口
ss.bind(("", 8080))
while True:
# 阻塞等待接收客户端发来的数据, 返回元祖 (数据, 客户端地址)
data, address = ss.recvfrom(1024)
# 打印 客户端地址 和 收到的数据
print("%s: %s" % (address, data.decode("UTF-8")))
# 回复数据到客户端(连接必须还没有关闭)
ss.sendto("收到".encode("UTF-8"), address)
# 关闭 服务端 Socket
# ss.close()
UDP 客户端:
import socket
# 创建面向非连接(UDP)客户端 Socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 服务端地址 和 端口
address = ("localhost", 8080)
# 发送数据 到 指定地址和端口
s.sendto("你好 Hello".encode("UTF-8"), address)
# 阻塞接收服务端回复的数据, 返回元祖 (数据, 服务端地址)
data, addr = s.recvfrom(1024)
# 打印 服务端地址 和 回复的数据
print("%s: %s" % (addr, data.decode("UTF-8")))
# 关闭 Socket
s.close()
socket 不再需要使用时必须确保被关闭(例如在 try finally 中关闭), socket 对象支持 with 上下文管理器, 可以使用 with 语句自动关闭 socket。
3. TCP 示例
TCP 服务端:
import socket
# 创建面向连接(TCP)服务端 Socket
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定 主机和端口, "" 表示本地所有可用接口
ss.bind(("", 8080))
# 开始监听 客户端 连接
ss.listen()
while True:
# 阻塞等待接收一个客户端连接, 返回元祖 (客户端socket, 客户端地址)
# conn: 与客户端建立的 socket 对象
# addr: 客户端地址, 元祖 (host, port)
conn, addr = ss.accept()
# 接收客户端发来的数据
data = conn.recv(1024)
# 打印 客户端地址 和 收到的数据
print("%s: %s" % (addr, data.decode("UTF-8")))
# 发送数据到客户端
conn.sendall("收到".encode("UTF-8"))
# 关闭 客户端 socket
conn.close()
# 关闭 服务端 Socket
# ss.close()
TCP 客户端:
import socket
# 创建面向非连接(TCP)客户端 Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务端地址 和 端口
address = ("localhost", 8080)
# 连接服务端
s.connect(address)
# 发送数据 到 服务端
s.sendall("你好 Hello".encode("UTF-8"))
# 阻塞接收服务端发送的数据
data = s.recv(1024)
# 打印 服务端发送的数据
print(data.decode("UTF-8"))
# 关闭 Socket
s.close()
socket 不再需要使用时必须确保被关闭(例如在 try finally 中关闭), socket 对象支持 with 上下文管理器, 可以使用 with 语句自动关闭 socket,代码示例:
"""
TCP 服务端
"""
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as ss:
ss.bind(("", 8080))
ss.listen()
while True:
conn, addr = ss.accept()
with conn:
data = conn.recv(1024)
print("%s: %s" % (addr, data.decode("UTF-8")))
conn.sendall("收到".encode("UTF-8"))
"""
TCP 客户端
"""
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("localhost", 8080))
s.sendall("你好 Hello".encode("UTF-8"))
data = s.recv(1024)
print(data.decode("UTF-8"))