【python】python进阶——网络编程
目录
引言
一、 网络编程基础
1.1 什么是网络编程?
1.2 基本概念
二、 Socket编程
2.1 创建Socket
2.2 TCP Socket工作流程
2.3 UDP Socket工作流程
三、高级网络编程框架
3.1 socketserver模块
3.2 异步网络编程
四、简单的聊天应用实现
五、常见问题与解决方案
5.1 端口占用问题
5.2 处理连接中断
5.3 设置超时
附录:Python网络编程API参考
socket模块核心API
Socket对象方法
socketserver模块核心类
asyncio网络编程核心API
select模块核心API
常见常量参数
引言
网络编程是现代软件开发中不可或缺的一部分,而Python以其简洁的语法和强大的库支持,成为了网络编程的理想选择。
一、 网络编程基础
1.1 什么是网络编程?
网络编程是指编写能够在网络环境中运行的程序,这些程序可以跨越不同的设备进行通信和数据交换。
1.2 基本概念
-
IP地址:设备的网络标识
-
端口:应用程序的通信端点(0-65535)
-
协议:通信规则(TCP、UDP、HTTP等)
-
Socket:网络通信的基础接口
二、 Socket编程
Socket是网络编程的核心,提供了不同主机间进程通信的端点。
2.1 创建Socket
import socket
# 创建TCP socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
参数说明:
AF_INET
:IPv4地址族
SOCK_STREAM
:TCP协议
SOCK_DGRAM
:UDP协议
2.2 TCP Socket工作流程
服务器端:
import socket
# 创建TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口
server_socket.bind(('localhost', 8888))
# 开始监听
server_socket.listen(5)
print("服务器启动,等待连接...")
while True:# 接受客户端连接client_socket, addr = server_socket.accept()print(f"接收到来自 {addr} 的连接")# 接收数据data = client_socket.recv(1024)print(f"收到数据: {data.decode()}")# 发送响应client_socket.send("你好,客户端!".encode())# 关闭连接client_socket.close()
客户端:
import socket
# 创建TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
client_socket.connect(('localhost', 8888))
# 发送数据
client_socket.send("你好,服务器!".encode())
# 接收响应
response = client_socket.recv(1024)
print(f"服务器响应: {response.decode()}")
# 关闭连接
client_socket.close()
2.3 UDP Socket工作流程
服务器端:
import socket
# 创建UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定地址和端口
udp_socket.bind(('localhost', 9999))
print("UDP服务器启动,等待数据...")
while True:# 接收数据data, addr = udp_socket.recvfrom(1024)print(f"收到来自 {addr} 的数据: {data.decode()}")# 发送响应udp_socket.sendto("收到消息!".encode(), addr)
客户端:
import socket
# 创建UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送数据
udp_socket.sendto("你好,UDP服务器!".encode(), ('localhost', 9999))
# 接收响应
response, addr = udp_socket.recvfrom(1024)
print(f"服务器响应: {response.decode()}")
# 关闭socket
udp_socket.close()
三、高级网络编程框架
3.1 socketserver模块
Python提供了更高级的socketserver模块,简化了网络服务器的创建。
TCP服务器示例:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):def handle(self):# 处理请求self.data = self.request.recv(1024).strip()print(f"{self.client_address[0]} 发送: {self.data.decode()}")# 发送响应self.request.sendall(self.data.upper())
if __name__ == "__main__":HOST, PORT = "localhost", 9999# 创建TCP服务器with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:print(f"服务器启动在 {HOST}:{PORT}")server.serve_forever()
3.2 异步网络编程
对于高性能网络应用,Python提供了asyncio模块。
异步TCP服务器示例:
import asyncio
async def handle_client(reader, writer):data = await reader.read(100)message = data.decode()addr = writer.get_extra_info('peername')print(f"收到来自 {addr} 的消息: {message}")writer.write(f"已接收: {message}".encode())await writer.drain()writer.close()
async def main():server = await asyncio.start_server(handle_client, '127.0.0.1', 8888)addr = server.sockets[0].getsockname()print(f'服务器运行在 {addr}')async with server:await server.serve_forever()
asyncio.run(main())
四、简单的聊天应用实现
一个简单的多客户端聊天服务器示例:
import socket
import threading
class ChatServer:def __init__(self, host='localhost', port=8888):self.host = hostself.port = portself.clients = []self.nicknames = []self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)def broadcast(self, message):for client in self.clients:try:client.send(message)except:self.remove_client(client)def remove_client(self, client):if client in self.clients:index = self.clients.index(client)self.clients.remove(client)nickname = self.nicknames[index]self.nicknames.remove(nickname)self.broadcast(f"{nickname} 离开了聊天室!".encode())def handle_client(self, client):while True:try:message = client.recv(1024)self.broadcast(message)except:self.remove_client(client)breakdef receive(self):self.server_socket.bind((self.host, self.port))self.server_socket.listen()print(f"服务器运行在 {self.host}:{self.port}")while True:client, address = self.server_socket.accept()print(f"连接到: {str(address)}")client.send("NICK".encode())nickname = client.recv(1024).decode()self.nicknames.append(nickname)self.clients.append(client)print(f"用户昵称: {nickname}")self.broadcast(f"{nickname} 加入了聊天室!".encode())client.send("连接到服务器!".encode())thread = threading.Thread(target=self.handle_client, args=(client,))thread.start()
if __name__ == "__main__":server = ChatServer()server.receive()
五、常见问题与解决方案
5.1 端口占用问题
# 设置SO_REUSEADDR选项解决端口占用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
5.2 处理连接中断
try:data = client_socket.recv(1024)if not data:print("连接已关闭")break
except ConnectionResetError:print("客户端连接中断")
5.3 设置超时
# 设置接收超时
client_socket.settimeout(30.0) # 30秒超时
附录:Python网络编程API参考
下面表格提供了Python网络编程中最常用API的完整参考,包括参数和返回值信息,可以帮助开发者更高效地使用Python进行网络编程。
socket模块核心API
API | 描述 | 参数 | 返回值 | 示例 |
---|---|---|---|---|
s.socket(family, type, proto) | 创建新的socket对象 |
SOCK_DGRAM)
| socket对象 |
|
s.gethostname() | 获取当前主机名 | 无 | 字符串形式的主机名 | host = socket.gethostname() |
s.gethostbyname(hostname) | 通过主机名获取IP地址 | hostname : 主机名字符串 | IP地址字符串 | ip = socket.gethostbyname('www.example.com') |
s.gethostbyaddr(ip_address) | 通过IP地址获取主机信息 | ip_address : IP地址字符串 | (hostname, aliaslist, ipaddrlist)元组 | info = socket.gethostbyaddr('8.8.8.8') |
s.getservbyname(servicename, protocolname) | 通过服务名获取端口号 |
| 端口号整数 | port = socket.getservbyname('http', 'tcp') |
s.getservbyport(port, protocolname) | 通过端口号获取服务名 |
| 服务名字符串 | service = socket.getservbyport(80, 'tcp') |
s.inet_aton(ip_string) | 将IPv4地址转换为32位打包二进制格式 |
IPv4地址字符串 | 打包的二进制数据(4字节) | packed = socket.inet_aton('192.168.1.1') |
s.inet_ntoa(packed_ip) | 将32位打包的IPv4地址转换为字符串格式 | packed_ip : 打包的二进制IP地址 | IPv4地址字符串 | ip = socket.inet_ntoa(packed) |
s.inet_pton(address_family, ip_str) | 将IP地址转换为打包二进制格式 |
| 打包的二进制数据 | packed = socket.inet_pton(socket.AF_INET6, '::1') |
s.inet_ntop(address_family, packed_ip) | 将打包的二进制IP地址转换为字符串格式 |
二进制IP地址 | IP地址字符串 |
|
Socket对象方法
方法 | 描述 | 参数 | 返回值 | 适用协议 |
---|---|---|---|---|
bind(address) | 将socket绑定到地址 | address : (host, port)元组 | 无 | TCP/UDP |
listen(backlog) | 启动TCP监听 | backlog : 最大挂起连接数 | 无 | TCP |
accept() | 接受TCP连接 | 无 | (socket, address)元组 | TCP |
connect(address) | 连接到远程socket | address : (host, port)元组 | 无 | TCP |
connect_ex(address) | connect()的扩展版本,出错时返回错误码而非异常 | address : (host, port)元组 | 错误代码(成功时为0) | TCP |
send(data) | 发送TCP数据 | data : 要发送的字节数据 | 已发送的字节数 | TCP |
recv(bufsize) | 接收TCP数据 | bufsize : 最大接收字节数 | 接收到的数据字节 | TCP |
sendall(data) | 完整发送TCP数据 | data : 要发送的字节数据 | 无(成功)/抛出异常(失败) | TCP |
sendto(data, address) | 发送UDP数据 |
| 已发送的字节数 | UDP |
recvfrom(bufsize) | 接收UDP数据 | bufsize : 最大接收字节数 | (data, address)元组 | UDP |
close() | 关闭socket | 无 | 无 | TCP/UDP |
settimeout(value) | 设置超时时间(秒) | value : 超时时间(浮点数) | 无 | TCP/UDP |
gettimeout() | 获取超时设置 | 无 | 超时时间(秒), 未设置返回None | TCP/UDP |
setsockopt(level, optname, value) | 设置socket选项 |
| 无 | TCP/UDP |
getsockopt(level, optname) | 获取socket选项 |
| 选项值 | TCP/UDP |
setblocking(flag) | 设置阻塞或非阻塞模式 |
False为非阻塞 | 无 | TCP/UDP |
getpeername() | 返回远程地址 | 无 | (ip, port)元组 | TCP |
getsockname() | 返回socket自己的地址 | 无 | (ip, port)元组 | TCP/UDP |
shutdown(how) | 关闭连接的一部分 |
SHUT_WR/ SHUT_RDWR | 无 | TCP |
socketserver模块核心类
类 | 描述 | 主要方法 | 返回值 |
---|---|---|---|
server.TCPServer | TCP同步服务器基类 | serve_forever() : 持续处理请求 handle_request() : 处理单个请求 shutdown() : 停止服务 | 无 |
server.UDPServer | UDP同步服务器基类 | 同上 | 同上 |
server.ThreadingMixIn | 线程混合类,用于创建多线程服务器 | 与服务器类混合使用 | 无 |
server.ForkingMixIn | 进程混合类,用于创建多进程服务器 | 与服务器类混合使用 | 无 |
server.BaseRequestHandler | 请求处理程序基类 |
| 无 |
asyncio网络编程核心API
API | 描述 | 参数 | 返回值 |
---|---|---|---|
asyncio.start_server(client_connected_cb, host, port) | 创建TCP服务器 |
客户端连接回调
| Server对象 |
asyncio.open_connection(host, port) | 创建TCP客户端连接 |
| (reader, writer)元组 |
asyncio.start_unix_server(client_connected_cb, path) | 创建Unix域套接字服务器 |
| Server对象 |
asyncio.open_unix_connection(path) | 创建Unix域套接字客户端连接 | path : 套接字路径 | (reader, writer)元组 |
asyncio.StreamReader.read(n) | 从流中读取数据 | n : 要读取的字节数 | 读取的数据(bytes) |
asyncio.StreamReader.readline() | 从流中读取一行 | 无 | 读取的行(bytes) |
asyncio.StreamWriter.write(data) | 向流中写入数据 | data : 要写入的数据(bytes) | 无 |
asyncio.StreamWriter.drain() | 等待写入操作完成 | 无 | 无 |
asyncio.StreamWriter.close() | 关闭流 | 无 | 无 |
select模块核心API
API | 描述 | 参数 | 返回值 |
---|---|---|---|
select.select(rlist, wlist, xlist[, timeout]) | 等待I/O就绪 |
| (rready, wready, xready)三元组 |
select.poll() | 创建poll对象 | 无 | poll对象 |
poll.register(fd[, eventmask]) | 注册文件描述符到poll对象 |
| 无 |
poll.unregister(fd) | 从poll对象中取消注册文件描述符 | fd : 文件描述符 | 无 |
poll.poll([timeout]) | 等待事件 | timeout : 超时时间(毫秒) | [(fd, event), ...]列表 |
常见常量参数
常量 | 值 | 描述 |
---|---|---|
socket.AF_INET | 2 | IPv4地址族 |
socket.AF_INET6 | 10 | IPv6地址族 |
socket.SOCK_STREAM | 1 | TCP协议类型 |
socket.SOCK_DGRAM | 2 | UDP协议类型 |
socket.SOL_SOCKET | 1 | Socket选项级别 |
socket.SO_REUSEADDR | 2 | 地址重用选项 |
socket.SO_KEEPALIVE | 9 | 保持连接选项 |
socket.SHUT_RD | 0 | 关闭接收方向 |
socket.SHUT_WR | 1 | 关闭发送方向 |
socket.SHUT_RDWR | 2 | 关闭双向连接 |
参考
-
官方文档
-
菜鸟教程 - Python 网络编程
-
廖雪峰 Python 教程 - TCP/UDP 编程