并发网络通信模型

1. 常见网络模型

  1. 循环服务器模型 :循环接收客户端请求,处理请求。同一时刻只能处理一个请求,处理完毕后再处理下一个。

优点:实现简单,占用资源少
缺点:无法同时处理多个客户端请求

适用情况:处理的任务可以很快完成,客户端无需长期占用服务端程序。udp比tcp更适合循环。

  1. 多进程/线程网络并发模型:每当一个客户端连接服务器,就创建一个新的进程/线程为该客户端服务,客户端退出时再销毁该进程/线程。

优点:能同时满足多个客户端长期占有服务端需求,可以处理各种请求。
缺点: 资源消耗较大

适用情况:客户端同时连接量较少,需要处理行为较复杂情况。

  1. IO并发模型:利用IO多路复用,异步IO等技术,同时处理多个客户端IO请求。

优点 : 资源消耗少,能同时高效处理多个IO行为
缺点 : 只能处理并发产生的IO事件,无法处理cpu计算

适用情况:HTTP请求,网络传输等都是IO行为。

2. 基于fork的多进程网络并发模型

实现步骤

  1. 创建监听套接字
  2. 等待接收客户端请求
  3. 客户端连接创建新的进程处理客户端请求
  4. 原进程继续等待其他客户端连接
  5. 如果客户端退出,则销毁对应的进程

代码演示

服务端

"""
fork_server多进程并发
重点代码
1. 创建监听套接字
2. 等待接收客户端请求
3. 客户端连接创建新的进程处理客户端请求
4. 原进程继续等待其他客户端连接
5. 如果客户端退出,则销毁对应的进程
"""

from socket import *
import os
import signal

# 全局变量
HOST = '0.0.0.0'
PORT = 25252
ADDR = (HOST, PORT)

# 具体处理客户端请求
def handle(c):
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.send(b'ok')
c.close()



# 1. 创建监听套接字
s = socket()
# 设置套接字端口可以立即重用
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)

# 处理僵尸进程
# 子进程 交给系统去处理
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
print('Listen the prot 8888....')



while True:
# 循环处理客户端连接
try:
c, addr = s.accept()
print('Connect from', addr)
except KeyboardInterrupt:
os._exit(0)
except Exception as e:
print(e)
continue

# 创建子进程处理客户端事物
pid = os.fork()
if pid == 0:
# 处理具体事物
s.close()
handle(c)
# 子进程处理完事物 销毁
os._exit(0)
# 无论父进程还是fork出错都要回去继续处理连接
else:
# 父进程不需要和客户端通信
c.close()

客户端

"""
tcp_client.py tcp 客户端流程
重点代码
"""

from socket import *

# 创建tcp套接字
sockfd = socket()

# 连接服务器
server_addr = ('127.0.0.1', 25252)
sockfd.connect(server_addr)


while True:
# 发送消息
data = input('Msg>>')
if not data:
break
sockfd.send(data.encode())
data = sockfd.recv(1024)
print('server:',data.decode())

sockfd.close()

最终如图

python 并发网络通信模型_python

3. 基于process的多进程网络并发模型

代码演示

服务端

from multiprocessing import Process
from socket import *
import os
import signal



# 具体处理客户端请求
def handle(c):
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.send(b'ok')
c.close()


# 全局变量
HOST = '0.0.0.0'
PORT = 25252
ADDR = (HOST, PORT)
# 1. 创建监听套接字
s = socket()
# 设置套接字端口可以立即重用
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)

# 处理僵尸进程
# 子进程 交给系统去处理
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
print('Listen the prot 8888....')


if __name__ == '__main__':

while True:
# 循环处理客户端连接
try:
c, addr = s.accept()
print('Connect from', addr)
except KeyboardInterrupt:
os._exit(0)
except Exception as e:
print(e)
continue

# 创建进程对象
p = Process(target=handle, args=(c,))
p.daemon = True
# 启动进程
p.start()
c.close()

4. 基于threeing的多线程网络并发模型

代码演示

服务器端

from threading import Thread
from socket import *
import sys


# 具体处理客户端请求
def handle(c):
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.send(b'ok')
c.close()


# 全局变量
HOST = '0.0.0.0'
PORT = 25252
ADDR = (HOST, PORT)
# 1. 创建监听套接字
s = socket()
# 设置套接字端口可以立即重用
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)

print('Listen the prot 8888....')


if __name__ == '__main__':

while True:
# 循环处理客户端连接
try:
c, addr = s.accept()
print('Connect from', addr)
except KeyboardInterrupt:
sys.exit('退出服务器')
except Exception as e:
print(e)
continue

# 创建线程对象
t = Thread(target=handle, args=(c,))
t.setDaemon(True)
# 启动线程
t.start()