服务器模型
2019-04-20 本文已影响0人
遇明不散
服务器
硬件服务器
计算机主机
软件服务器
- 网络服务器,提供后端逻辑服务和请求处理的程序集合及架构
- 编写的服务端应用程序,在硬件服务器上运行,一般依托于操作系统,给用户提供一套完整的服务
服务器架构
服务器的组织形式
c/s 客户端服务器模型
b/s 浏览器服务器模型
服务器目标
- 处理速度更快,并发量更高,安全性更强
- 硬件:更高的配置,更好的集成分布技术,更好的网络优 化和网络安全技术
- 软件:占用资源更少,运行更稳定,算法更优良,安全性 更好,并发性更高,更容易扩展
常见服务器
- httpserver:处理http请求
- webserver:网站的后端应用服务器程序
- 邮箱服务器:邮件处理
- ftp文件服务器:文件的上传下载
功能:网络连接,逻辑处理,数据交互,数据传输,协议的实现
服务器模型
循环服务器模型
- 循环接收客户端请求,处理请求。同一时刻只能处理一个请求,处理完毕后再处理下一个
- 实现简单,占用资源少
- 不能同时处理多个客户端请求,不允许某个客户端长期占用服务器资源
- 由于UDP不需要进行连接,所以循环服务器模型更加适合UDP通信
并发服务器模型
能够同时处理多个客户端请求,每有一个客户端就创建一个进程或线程处理客户端的具体请求事件,而主进程或主线程继续接受其他客户端的连接
IO并发(IO多路复用)
- 优点:资源消耗少,IO处理速度快
- 缺点:不能适用cpu密集型程序
多进程/多线程并发
- 为每个客户端创建单独的进程线程,执行请求
- 优点:每个客户端可以长期占有服务器运行程序,能够使用多核资源,可以处理IO或者cpu运算
- 缺点:消耗系统资源高
多进程并发服务器模型
使用fork实现多进程并发
- 创建套接字,绑定,监听
- 主进程等待接收客户端请求
- 创建新的子进程处理客户端请求
- 原有主进程继续等待接收新的客户端连接
- 如果客户端退出则关闭子进程
# fork_server.py
from socket import *
import sys
import signal
import os
# 主机地址与端口
HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
# 主进程忽略子进程的退出,子进程退出自动由系统处理
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
# 客户端处理函数
def client_handler(c):
print('服务器已连接到客户端:',c.getpeername())
try:
while True:
data = c.recv(1024)
if not data:
break
print('%s说:%s'%(c.getpeername(),data.decode()))
c.send('收到客户端请求'.encode())
except (KeyboardInterrupt,SystemError):
sys.exit('客户端退出')
except Exception as e:
print('服务器内部错误信息:',e)
c.close()
sys.exit(0)
# 创建套接字
s = socket(AF_INET,SOCK_STREAM,0)
# 设置端口可重用
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
# 绑定
s.bind(ADDR)
# 监听
s.listen(5)
# 主进程等待接收客户端请求
print('进程%d正在等待接受请求:'%os.getpid())
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
sys.exit('服务器退出,暂无服务可连')
except Exception as e:
print('错误信息:',e)
continue
# 创建新的子进程处理客户端请求
pid = os.fork()
# 判断是否是子进程
if pid == 0:
s.close()
client_handler(c)
# 如果不是子进程或者创建失败,则继续等待接受其他客户端
else:
c.close()
continue
使用multiprocessing模块实现多进程并发
from socket import *
import os,sys
from multiprocessing import Process
import traceback
def handler():
print("Connect from",c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b'Receive request')
c.close()
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8899))
s.listen(5)
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
s.close()
sys.exit("服务器退出")
except Exception:
traceback.print_exc()
continue
t = Process(target = handler)
t.daemon = True
t.start()
多线程并发服务器模型
对比多进程并发
- 消耗资源较少
- 线程应该更注意共享资源的操作
- 在python中应该注意GIL问题,网络延迟较高,线程并发也是一种可行的办法
实现步骤
- 创建套接字,绑定监听
- 接收客户端请求,创建新的线程
- 主线程继续接收其他客户端连接
- 分支线程启动对应的函数处理客户端请求
- 当客户端断开,则分支线程结束
from socket import *
import os,sys
from threading import *
import traceback
def handler(c):
print("Connect from",c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b'Receive request')
c.close()
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8899))
s.listen(5)
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
s.close()
sys.exit("服务器退出")
except Exception:
traceback.print_exc()
continue
t = Thread(target = handler,args = (c,))
t.daemon = True
t.start()
补充
import os
# 获取目录中文件列表
os.listdir(path)
# 判断是否为普通文件
os.path.isfile()
# 判断是否为目录
os.path.isdir()
import traceback
# 更详细的打印异常信息
traceback.print_exc()
集成模块socketserver
通过模块的不同类的组合完成多进程/多线程的TCP/UDP的并发
套接字请求
StreamRequestHandler
处理tcp套接字请求
DatagramRequestHandler
处理udp套接字请求
创建套接字
TCPServer
创建tcp server
UDPServer
创建udp server
创建多进程
ForkingMixIn
ForkingTCPServer <--> ForkingMinIn + TCPServer
ForkingUDPServer <--> ForkingMinIn + UDPServer
创建多线程
ThreadingMixIn
ThreadingTCPServer <--> ThreadingMinIn + TCPServer
ThreadingUDPServer <--> ThreadingMinIn + UDPServer
步骤
- 创建服务器类
- 创建处理类
- 使用创建的服务器类来产生服务器