【本文导读】文中有许多不妥之处,敬请批评指正!python编写的服务端,有八种实现并发的方式,如阻塞(对等)套接字实现并发、非阻塞套接字实现并发、epoll实现并发、多进程实现并发、多线程实现并发、进程池实现并发、线程池实现并发、协程实现并发等。

一、什么是并发?

1、套接字:是python与操作系统两者间的一个介面,通过设置对等的IP与埠,实现数据的发送与接收。套接字有三种:服务端监听套接字、服务端对等套接字、客户端(对等)套接字,其中服务端对等套接字与客户端套接字实现数据的传输。

2、阻塞:套接字传输数据时,会出现accept与recv两种阻塞,服务端在没有新的套接字来之前,不能处理已经建立连接的套接字的请求,此时出现accept阻塞;服务端在没有接受到客户端请求数据之前,不能与其他客户端建立连接,此时出现recv阻塞,客户端也会出现类似recv阻塞。

3、进程与并行:多进程实现并行,遵循轮巡调度机制,由多个cpu同时运行,有效解决阻塞问题。并行属于并发。

4、线程与并发:多线程实现并发,遵循轮巡调度机制,一个cpu在一定时间内的并行,由python解释器调度。

二、实现并发的八种方式

(一)阻塞(对等)套接字实现并发的服务端

1、阻塞(对等)套接字实现并发的服务端

# 阻塞(对等)套接字实现并发的服务端
import socket # 导入模块socket(API介面)
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,8881)) # 绑定ip与埠
server.listen(5) # 开始监听
while True:
conn, address = server.accept() # 获取服务端对等套接字
while True: # 向客户端接收发送数据
client_data = conn.recv(1000)
if client_data:
print("客户端发来的数据",client_data.decode())
conn.send(client_data)
else:
con.close() # 关闭服务端
break

(二)非阻塞套接字实现并发的服务端

1、非阻塞套接字实现并发的服务端:

# 非阻塞套接字实现并发的服务端
import socket # 导入模块socket
server = socket.socket() # 创建服务端
server.setblocking(False) # 套接字设成非阻塞
server.bind((0.0.0.0,8882)) # 绑定ip与埠
server.listen(5) # 开始监听
conn_list = [] # 套接字列表
while True:
try:
conn,address = server.accept() # 获取服务端对等套接字
conn.setblocking(False) # 套接字设置成非阻塞
conn_list.append(conn) # 套接字放入列表
except BlockingIOError as error: # 捕获异常
pass
for i in conn_list:
try: # 向客户端接收发送数据
recv_data = conn.recv(1024)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close() # 关闭服务端
conn_list.remove(conn) # 移除套接字
except BlockingIOError as error: #捕获异常
pass

(三)epoll实现并发的服务端

1、通过epoll实现并发的服务端

# 通过epoll(io多路复用)实现并发的服务端
import socket # 导入模块
import selectors # 导入模块
epoll_selector = selectors.EpollSelector() # 实例化epoll(liunx)
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,8888)) # 绑定ip与埠
server.listen(1000) # 开始监听
def recv(conn): # 向客户端接收或发送数据
recv_data = conn.recv(1024)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
epoll_selector.unregister(conn) # 取消事件
conn.close() # 关闭对等套接字
def accept(server):
conn,addr = server.accept() # 获取服务端对等套接字
# 回调函数处理第二个recv阻塞,注册事件:操作系统监控已获取conn、数据等
epoll_selector.register(conn,selectors.EVENT_READ,recv)
# 回调函数处理第一个accept阻塞,注册事件:操作系统监控已获取server、数据等
epoll_selector.register(server,selectors.EVENT_READ,accept)
while True: # 事件循环
events = epoll_selector.select() # 查询准备好的事件,返回列表
# 用print(events)查询事件内容
for key ,mask in events: # 拆包,获取socket、
callback = key.data # 获取accept
sock = key.fileobj # 获取准备好的客户端套接字
callback(sock) # 调用accept函数

(四)多进程实现并发的服务端

1、多进程实现并发的服务端

# 多进程实现并发的服务端
import socket # 导入模块
from multiprocessing import Process # 导入多进程模块
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,9091)) # 绑定ip与埠
server.listen(1000) # 开始监听
def fun(conn): # 向客户端接收或发送数据
while True:
recv_data = conn.recv(1024)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close()
break
while True: # 主进程
conn,addr = server.accept() # 获取服务端对等套接字
p = Process(target=fun,args=(conn,)) # 实例化子进程(连接客户端)
p.start() # 开启子进程

(五)多线程实现并发的服务端

1、多线程实现并发的服务端:

# 多线程实现并发的客户端
import socket # 导入模块
import threading # 导入多线程模块
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,6668)) # 绑定ip与埠
server.listen(1000) # 开始监听
def fun(conn): # 向客户端接收或发送数据
while True:
recv_data = conn.recv(1024)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close()
while True: # 主线程
conn,address = server.accept() # 获取服务端对等套接字
t = threading.Thread(target=fun,args=(conn,)) # 实例化子线程(连接客户端)

(六)使用进程池(进程池)实现并发的服务端

1、使用进程池(进程池)实现并发的服务端

# 使用进程池(进程池)实现并发的服务端
import socket # 导入模块
from multiprocessing import Pool,cpu_count # 导入进程池
from multiprocessing.pool import ThreadPool # 导入线程池
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,5678)) # 绑定ip与埠
server.listen(1000) # 开始监听
def work_thread(conn): # 向客户端接收或发送数据
while True:
recv_data = conn.recv(1000)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close()
break
def work_process(server):
t_pool = ThreadPool(2*cpu_count()) # 准备线程
while True:
conn,address = server.accept() # 获取服务端对等连接套接字
t_pool.apply_async(work_thread,args=(conn,))# 进程池(客户端收发数据)
n = cpu_count() # CPU的核数
p = Pool(n) # 实例化进程池
for i in range(n):
p.apply_async(work_process,args=(server,)) #进程池(accept阻塞)
p.close()
p.join()

(七)使用进程池(多线程)实现并发的服务端

1、使用进程池(多线程)实现并发的服务端

# 使用进程池(多线程)实现并发的服务端
import socket # 导入模块
from multiprocessing import Pool,cpu_count
from threading import Thread # 导入多线程
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,7777)) # 绑定ip与埠
server.listen(1000) # 开始监听
def work_thread(conn): # 向客户端接收发送数据
while True:
recv_data = conn.recv(1000)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close()
break
def work_process(server):
while True:
conn,addr = server.accept() # 获取服务端对等套接字
t = Thread(target=work_thread,args=(conn,)) # 多线程
t.start()
n = cpu_count()
p = Pool(n)
for i in range(n):
p.apply_async(work_process,args=(server,)) # 进程池(accept阻塞)
p.close()
p.join()

(八)协程实现并发的服务端

1、使用协程实现并发的服务端

# 使用协程实现并发的服务端
from gevent import monkey;monkey.patch_socket() # 替换python自带的socket
import gevent # 导入协程
import socket # 导入模块
server = socket.socket() # 创建服务端
server.bind((0.0.0.0,8989)) # 绑定ip与埠
server.listen(1000) # 开始监听
def worker(conn): # 向客户端接收发送数据
while True:
recv_data = conn.recv(1000)
if recv_data:
print("客户端发来的数据",recv_data.decode())
conn.send(recv_data)
else:
conn.close()
break
while True:
conn,address = server.accept() # 获取服务端对等套接字
gevent.spawn(worker,conn) # 实例化协程,并将conn作为参数传入

三、实现并发的具体操作:

1、先启动服务端(以协程并发为例),埠自己设置。

2、再启动两个客户端,输入数据,与服务端实现数据传输。


推荐阅读:
相关文章