pin_drop当前位置:知识文库 ❯ 图文
Socket编程基础 - Python网络编程入门教程
一、什么是Socket
Socket(套接字)是网络编程的核心概念,它是应用程序与网络协议栈之间的通信接口。可以把Socket想象为电话系统:Socket相当于电话机,IP地址是电话号码,而端口号则是分机号。
在Python中,我们使用内置的socket模块来创建和操作Socket,它提供了对底层网络通信的抽象,让开发者可以专注于业务逻辑而非网络细节。
小贴士
Socket概念起源于1983年加州大学伯克利分校发布的BSD Unix系统,后来成为POSIX标准的一部分。Python的socket模块是对C语言socket API的封装,保持了接口的一致性。
二、Python中的socket模块
Python的socket模块是标准库的一部分,无需额外安装即可使用。它支持多种地址族和套接字类型,是构建网络应用的基础工具。
导入socket模块
代码示例
import socket
# 创建TCP Socket(IPv4)
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建UDP Socket(IPv4)
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 创建IPv6 Socket
ipv6_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)常用Socket常量
-
socket.AF_INET:IPv4地址族
-
socket.AF_INET6:IPv6地址族
-
socket.SOCK_STREAM:TCP流式套接字
-
socket.SOCK_DGRAM:UDP数据报套接字
-
socket.SOCK_RAW:原始套接字
三、TCP与UDP协议对比
Socket编程主要涉及两种传输层协议:TCP和UDP。它们在网络通信中扮演着不同的角色,适用于不同的场景。
四、地址族与套接字类型
在创建Socket时,需要指定地址族和套接字类型。地址族决定了使用IP协议版本,套接字类型决定了传输层协议。
地址族(Address Family)
地址族定义了网络地址的格式。最常用的是AF_INET(IPv4)和AF_INET6(IPv6)。IPv4地址格式为('127.0.0.1', 8080),而IPv6地址格式为('::1', 8080, 0, 0)。
套接字类型(Socket Type)
套接字类型定义了数据传输的方式。TCP使用SOCK_STREAM提供字节流服务,UDP使用SOCK_DGRAM提供数据报服务。
代码示例
# 查看可用的地址族和套接字类型
import socket
print("可用的地址族:")
print(f"AF_INET: {socket.AF_INET}")
print(f"AF_INET6: {socket.AF_INET6}")
print("\n可用的套接字类型:")
print(f"SOCK_STREAM: {socket.SOCK_STREAM}")
print(f"SOCK_DGRAM: {socket.SOCK_DGRAM}")
print(f"SOCK_RAW: {socket.SOCK_RAW}")五、Socket编程基本流程
无论是TCP还是UDP编程,Socket的生命周期都遵循一定的模式。了解这些流程有助于编写健壮的网络应用。
TCP Socket流程
-
服务端:创建Socket → 绑定地址 → 监听连接 → 接受连接 → 收发数据 → 关闭连接
-
客户端:创建Socket → 连接服务端 → 收发数据 → 关闭连接
UDP Socket流程
-
接收方:创建Socket → 绑定地址 → 接收数据
-
发送方:创建Socket → 发送数据(指定目标地址)
常用Socket方法
代码示例
# 常用Socket方法一览
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址(服务端)
sock.bind(('localhost', 8080))
# 监听连接(TCP服务端)
sock.listen(5) # 最大等待连接数
# 接受连接(TCP服务端)
conn, addr = sock.accept()
# 连接服务器(TCP客户端)
sock.connect(('localhost', 8080))
# 发送数据
sock.send(b'Hello World')
sock.sendall(b'Complete message') # 确保全部发送
# 接收数据
data = sock.recv(1024)
# UDP发送/接收
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_sock.sendto(b'Hello', ('localhost', 8080))
data, addr = udp_sock.recvfrom(1024)
# 关闭Socket
sock.close()六、完整代码示例
示例1:TCP回显服务器
下面是一个完整的TCP回显服务器示例,客户端发送的任何消息都会被原样返回。
代码示例
# tcp_echo_server.py - TCP回显服务器
import socket
def start_echo_server(host='localhost', port=8080):
"""启动TCP回显服务器"""
# 创建TCP Socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_sock:
# 设置SO_REUSEADDR,允许端口重用
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定地址和端口
server_sock.bind((host, port))
# 开始监听
server_sock.listen(5)
print(f"服务器启动,监听 {host}:{port}")
while True:
# 等待客户端连接
conn, addr = server_sock.accept()
with conn:
print(f"客户端已连接: {addr}")
while True:
data = conn.recv(1024)
if not data:
break
print(f"收到: {data.decode('utf-8')}")
# 回显数据
conn.sendall(data)
if __name__ == '__main__':
start_echo_server()示例2:TCP客户端
代码示例
# tcp_echo_client.py - TCP回显客户端
import socket
def echo_client(message, host='localhost', port=8080):
"""连接到回显服务器并发送消息"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_sock:
# 连接服务器
client_sock.connect((host, port))
# 发送消息
client_sock.sendall(message.encode('utf-8'))
# 接收回显
data = client_sock.recv(1024)
print(f"服务器回显: {data.decode('utf-8')}")
if __name__ == '__main__':
echo_client("Hello, Socket!")示例3:UDP通信
代码示例
# udp_server.py - UDP服务器
import socket
def udp_server(host='localhost', port=9090):
"""UDP服务器"""
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp_sock:
udp_sock.bind((host, port))
print(f"UDP服务器启动,监听 {host}:{port}")
while True:
data, addr = udp_sock.recvfrom(1024)
print(f"收到 {addr}: {data.decode('utf-8')}")
# 发送响应
udp_sock.sendto(b"Message received", addr)
# udp_client.py - UDP客户端
def udp_client(message, host='localhost', port=9090):
"""UDP客户端"""
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp_sock:
udp_sock.sendto(message.encode('utf-8'), (host, port))
response, addr = udp_sock.recvfrom(1024)
print(f"服务器响应: {response.decode('utf-8')}")七、注意事项
注意1:始终使用
with语句管理Socket生命周期,确保连接正确关闭,避免资源泄漏。
注意2:TCP的
send()不保证发送全部数据,建议使用sendall()确保完整发送。
注意3:服务端应设置
SO_REUSEADDR选项,避免重启时报"Address already in use"错误。
注意4:处理网络数据时要注意异常处理,包括
ConnectionResetError、TimeoutError等网络异常。
八、小结与练习题
小结
-
Socket是网络通信接口:提供应用程序与网络协议栈之间的抽象层
-
TCP与UDP各有优势:TCP可靠有序,UDP快速高效
-
正确使用资源管理:使用with语句或显式close()释放Socket资源
练习题
练习1
编写一个TCP聊天程序,支持多客户端同时连接,实现服务器端的消息转发功能。要求使用threading模块处理并发连接。
练习2
编写一个简单的文件传输工具,使用TCP协议实现客户端上传文件到服务器的功能。需要考虑大文件分块传输和传输完成的确认机制。
常见问题
Socket和HTTP有什么区别?
Socket是传输层的通信接口,属于底层API;HTTP是应用层协议,运行在TCP之上。HTTP请求实际上是通过Socket发送和接收数据的。Python的requests或urllib库底层就是使用Socket来实现HTTP通信的。
为什么TCP服务器需要listen()?
listen()将被动Socket转换为监听Socket,内核会维护一个连接队列来存储等待accept()的客户端连接。listen()参数指定了队列的最大长度,超过此长度的新连接会被拒绝。
如何选择TCP还是UDP?
如果需要可靠传输(如文件传输、网页浏览),选择TCP;如果能容忍少量数据丢失但要求低延迟(如视频直播、语音通话),选择UDP。大多数应用场景使用TCP即可满足需求。
recv()的缓冲区大小如何设置?
recv(n)的n参数指定一次最多接收的字节数。常用值为1024或4096。如果数据超过缓冲区,需要多次调用recv()。对于定长消息,可以使用固定缓冲区大小;对于变长消息,通常需要定义消息边界(如长度前缀或结束符)。
什么是SO_REUSEADDR?为什么需要它?
SO_REUSEADDR允许Socket绑定到一个处于TIME_WAIT状态的端口。当服务器异常关闭时,端口可能仍然被占用,设置此选项后可以立即重启服务器而不必等待端口释放(通常需要1-4分钟)。
本文涉及AI创作
内容由AI创作,请仔细甄别