目录
[一、 网络编程基础](#一、 网络编程基础)
[1.1 什么是网络编程?](#1.1 什么是网络编程?)
[1.2 基本概念](#1.2 基本概念)
[二、 Socket编程](#二、 Socket编程)
[2.1 创建Socket](#2.1 创建Socket)
[2.2 TCP Socket工作流程](#2.2 TCP Socket工作流程)
[2.3 UDP Socket工作流程](#2.3 UDP Socket工作流程)
[3.1 socketserver模块](#3.1 socketserver模块)
[3.2 异步网络编程](#3.2 异步网络编程)
[5.1 端口占用问题](#5.1 端口占用问题)
[5.2 处理连接中断](#5.2 处理连接中断)
[5.3 设置超时](#5.3 设置超时)
引言
网络编程是现代软件开发中不可或缺的一部分,而Python以其简洁的语法和强大的库支持,成为了网络编程的理想选择。
一、 网络编程基础
1.1 什么是网络编程?
网络编程是指编写能够在网络环境中运行的程序,这些程序可以跨越不同的设备进行通信和数据交换。
1.2 基本概念
-
IP地址:设备的网络标识
-
端口:应用程序的通信端点(0-65535)
-
协议:通信规则(TCP、UDP、HTTP等)
-
Socket:网络通信的基础接口
二、 Socket编程
Socket是网络编程的核心,提供了不同主机间进程通信的端点。
2.1 创建Socket
python
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工作流程
服务器端:
python
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()
客户端:
python
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工作流程
服务器端:
python
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)
客户端:
python
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服务器示例:
python
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服务器示例:
python
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())
四、简单的聊天应用实现
一个简单的多客户端聊天服务器示例:
python
import socket
import threading
class ChatServer:
def __init__(self, host='localhost', port=8888):
self.host = host
self.port = port
self.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)
break
def 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 端口占用问题
python
# 设置SO_REUSEADDR选项解决端口占用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
5.2 处理连接中断
python
try:
data = client_socket.recv(1024)
if not data:
print("连接已关闭")
break
except ConnectionResetError:
print("客户端连接中断")
5.3 设置超时
python
# 设置接收超时
client_socket.settimeout(30.0) # 30秒超时
附录:Python网络编程API参考
下面表格提供了Python网络编程中最常用API的完整参考,包括参数和返回值信息,可以帮助开发者更高效地使用Python进行网络编程。
socket模块核心API
API | 描述 | 参数 | 返回值 | 示例 |
---|---|---|---|---|
s.socket(family, type, proto) |
创建新的socket对象 | **family :**地址族(AF_INET/AF_INET6) type : socket类型(SOCK_STREAM/ SOCK_DGRAM) proto : 协议号(通常为0) |
socket对象 | s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) |
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) |
通过服务名获取端口号 | servicename : 服务名称(如'http') protocolname : 协议名称(如'tcp') |
端口号整数 | port = socket.getservbyname('http', 'tcp') |
s.getservbyport(port, protocolname) |
通过端口号获取服务名 | port : 端口号整数 protocolname : 协议名称(如'tcp') |
服务名字符串 | service = socket.getservbyport(80, 'tcp') |
s.inet_aton(ip_string) |
将IPv4地址转换为32位打包二进制格式 | ip_string : 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地址转换为打包二进制格式 | address_family : AF_INET或AF_INET6 ip_str : IP地址字符串 |
打包的二进制数据 | packed = socket.inet_pton(socket.AF_INET6, '::1') |
s.inet_ntop(address_family, packed_ip) |
将打包的二进制IP地址转换为字符串格式 | address_family : AF_INET或AF_INET6 packed_ip : 打包的 二进制IP地址 |
IP地址字符串 | ip=socket.inet_ntop (socket.AF_INET6, packed) |
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数据 | data : 要发送的字节数据 address : (host, port)元组 |
已发送的字节数 | 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选项 | level : 选项级别 optname : 选项名称 value : 选项值 |
无 | TCP/UDP |
getsockopt(level, optname) |
获取socket选项 | level : 选项级别 optname : 选项名称 |
选项值 | TCP/UDP |
setblocking(flag) |
设置阻塞或非阻塞模式 | flag : True为阻塞, False为非阻塞 |
无 | TCP/UDP |
getpeername() |
返回远程地址 | 无 | (ip, port)元组 | TCP |
getsockname() |
返回socket自己的地址 | 无 | (ip, port)元组 | TCP/UDP |
shutdown(how) |
关闭连接的一部分 | how : SHUT_RD/ SHUT_WR/ SHUT_RDWR |
无 | TCP |
socketserver模块核心类
类 | 描述 | 主要方法 | 返回值 |
---|---|---|---|
server.TCPServer |
TCP同步服务器基类 | serve_forever() : 持续处理请求 handle_request() : 处理单个请求 shutdown() : 停止服务 |
无 |
server.UDPServer |
UDP同步服务器基类 | 同上 | 同上 |
server.ThreadingMixIn |
线程混合类,用于创建多线程服务器 | 与服务器类混合使用 | 无 |
server.ForkingMixIn |
进程混合类,用于创建多进程服务器 | 与服务器类混合使用 | 无 |
server.BaseRequestHandler |
请求处理程序基类 | handle() : 必须重写的方法,处理请求 setup() : 初始化方法 finish() : 清理方法 |
无 |
asyncio网络编程核心API
API | 描述 | 参数 | 返回值 |
---|---|---|---|
asyncio.start_server(client_connected_cb, host, port) |
创建TCP服务器 | client_connected_cb : 客户端连接回调 host : 主机地址 port : 端口号 |
Server对象 |
asyncio.open_connection(host, port) |
创建TCP客户端连接 | host : 服务器主机地址 port : 服务器端口号 |
(reader, writer)元组 |
asyncio.start_unix_server(client_connected_cb, path) |
创建Unix域套接字服务器 | client_connected_cb : 客户端连接回调 path : 套接字路径 |
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就绪 | rlist : 等待读取的对象列表 wlist : 等待写入的对象列表 xlist : 等待异常的对象列表 timeout : 超时时间(秒) |
(rready, wready, xready)三元组 |
select.poll() |
创建poll对象 | 无 | poll对象 |
poll.register(fd[, eventmask]) |
注册文件描述符到poll对象 | fd : 文件描述符 eventmask : 事件掩码 |
无 |
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 | 关闭双向连接 |