1. 简介
Python中的socket模块提供了网络通信的支持,使得我们可以方便地编写客户端或服务端程序,进行数据传输或信息交换等操作。socket模块提供了两种类型的socket:基于流的TCP套接字和基于数据报的UDP套接字。
此外,在Python3中socket模块还提供了一个叫做socketserver
的高级模块,用于编写基于套接字的服务器程序,使得服务器程序编写更加简单和方便。
2. TCP和UDP协议
2.1 TCP协议
TCP协议是一种面向连接的协议,建立连接时需要经历“三次握手”的过程。在数据传输时,TCP协议保证数据的可靠性、有序性和不重复性。因此,TCP协议通常用于需要可靠传输的情况,例如文件传输、邮件发送等。
在Python中,创建TCP套接字可以使用socket.SOCK_STREAM
参数:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2.2 UDP协议
UDP协议是一种无连接的协议,不需要建立连接,直接发送数据包。由于UDP协议不保证数据传输的可靠性和有序性,因此其传输效率比TCP要高。UDP协议通常用于视频流、实时游戏等需要传输效率高的情况。
在Python中,创建UDP套接字可以使用socket.SOCK_DGRAM
参数:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
3. 套接字的基本操作
3.1 套接字的创建
在使用Python中的socket模块进行网络通信时,需要先创建套接字对象:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
其中,AF_INET
表示使用IPv4地址族,SOCK_STREAM
表示使用TCP协议(数据流套接字)。
3.2 连接服务器
如果要连接服务器,需要指定服务器的IP地址和端口号:
import socket
# 创建套接字对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
s.connect(("127.0.0.1", 8888))
# 发送数据
data = "Hello, server!"
s.send(data.encode())
# 接收数据
response = s.recv(1024)
print(response.decode())
# 关闭套接字
s.close()
在进行网络通信时,需要将Python的字符串类型转化为二进制数据进行传输,因此使用encode()
方法将字符串转化为二进制数据,使用decode()
方法将二进制数据转化为字符串。
3.3 绑定地址和端口号
如果要创建服务器端程序,需要先绑定服务器的地址和端口号:
import socket
# 创建套接字对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定地址和端口号
s.bind(("127.0.0.1", 8888))
# 监听端口号
s.listen(5)
while True:
# 等待客户端连接
conn, addr = s.accept()
print("Connected by", addr)
# 接收数据
data = conn.recv(1024).decode()
print(data)
# 发送数据
response = "Hello, client!"
conn.send(response.encode())
# 关闭套接字连接
conn.close()
绑定地址和端口号的方法是使用bind()
,该方法的参数是一个元组,包含要绑定的IP地址和端口号。
3.4 监听端口号
在创建服务器端程序时,还需要使用listen()
方法监听端口号。该方法的参数表示在请求队列中最多可以等待的连接数。
3.5 等待客户端连接
使用accept()
方法等待客户端连接。该方法返回一个新的套接字对象conn
和客户端的IP地址和端口号。
3.6 接收和发送数据
在进行通信后,需要使用套接字对象的recv()
方法接收数据,使用send()
方法发送数据。在接收和发送数据时,需要将二进制数据转化为字符串或将字符串转化为二进制数据。
3.7 关闭套接字
在通信完成后,需要调用套接字对象的close()
方法关闭套接字。
4. socketserver模块
除了使用socket模块编写服务器程序外,Python3还提供了一个高级模块socketserver,用于编写基于套接字的服务器程序,使得服务器程序编写更加简单和方便。
使用socketserver模块创建服务器程序需要继承socketserver.BaseRequestHandler
类,并重写handle()
方法。并且需要继承socketserver.TCPServer
类并指定请求处理器和服务端的地址和端口号。
import socketserver
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024).decode()
print(data)
response = "Hello, client!"
self.request.sendall(response.encode())
server = socketserver.TCPServer(("127.0.0.1", 8888), MyHandler)
server.serve_forever()
在这个示例中,MyHandler
类继承自socketserver.BaseRequestHandler
类,并重写了handle()
方法。该方法用于接收和发送数据。
然后,创建一个socketserver.TCPServer
对象,指定服务器地址和端口号以及请求处理器,调用serve_forever()
方法启动服务器。
5. 使用select模块进行异步I/O操作
通常情况下,如果客户端或服务器需要进行多任务处理,例如同时处理多个客户端请求,可以使用多线程或多进程技术来实现并行处理。但是,Python中还提供了一个非常有效的方法来实现异步I/O操作,那就是使用select
模块。
select
模块提供了一种可以同时监控多个文件对象和套接字对象的实时I/O模型,当其中一个文件对象或套接字对象准备就绪可读/可写时,就可以使用select()
方法通知程序进行操作。
使用select
模块实现基于套接字的异步I/O操作需要经过以下几个步骤:
创建套接字
绑定地址和端口号
设置监听
使用select()方法进行异步I/O操作
import socket
import select
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1", 8888))
s.listen(5)
inputs = [s]
outputs = []
while True:
rs, ws, es = select.select(inputs, outputs, inputs, 0.5)
for r in rs:
if r is s:
conn, addr = s.accept()
print("Connected by", addr)
inputs.append(conn)
else:
data = r.recv(1024).decode()
if not data:
r.close()
inputs.remove(r)
else:
print(data)
outputs.append(r)
for w in ws:
response = "Hello, client!"
w.sendall(response.encode())
outputs.remove(w)
for e in es:
if e in inputs:
inputs.remove(e)
if e in outputs:
outputs.remove(e)
e.close()
在这个示例中,创建了一个服务器程序,并使用select()
方法进行异步I/O操作。同时使用inputs
和outputs
列表保存套接字和输出信息。在 select.select()
方法中,第一个参数是输入列表,第二个参数是输出列表,第三个参数是发生错误的列表,第四个参数是超时时间。
结论
Python中socket网络通信提供了方便快捷的方法来进行服务端和客户端的数据传输和信息交换。而且Python提供的socketserver
模块和select
模块更进一步简化了网络通信编程的难度和复杂度。