PythonServer
服务器模型
- 硬件服务器
- 主机、集群
- 厂商:IBM、HP、联想、浪潮
- 软件服务器:编写的服务端应用程序,在硬件服务器上运行,一般依托于操作系统,给用户提供一套完整的服务
- httpserver:处理http请求
- webserver:网站的后端应用服务器程序
- 邮箱服务器:邮件处理
- ftp文件服务器:文件的上传下载
- 功能:网络连接、逻辑处理、数据交互、数据传输、协议的实现
- 结构:
- c/s 客户端服务器模型
- b/s 浏览器服务器模型
- 服务器目标:处理速度更快、并发量更高、安全性更强
- 硬件要求:
- 更高的配置
- 更好的集成分布技术
- 更好的网络优化
- 网络安全技术
- 软件要求:
- 占用资源更少
- 运行更稳定
- 算法更优良
- 安全性更好
- 并发性更高
- 更容易扩展
- 硬件要求:
循环模型
- 循环接收客户端请求,处理请求。同一时刻只能处理一个请求,处理完毕后再处理下一个
- 优点:实现简单,占用资源少
- 缺点:无法同时处理多个客户端任务
- 适用情况:处理的任务可以短时间完成,不需要建立并发,更适合udp使用
并发模型
- 能够同时处理多个客户端请求
IO并发:IO多路复用(例如:select)
- 优点:资源消耗少,IO处理速度快
- 缺点:不能适用cpu密集型程序
多进程/多线程并发
- 为每个客户端创建单独的进程线程,执行请求
- 优点:
- 每个客户端可以长期占有服务器运行程序
- 能够使用多核资源,可以处理IO或者cpu运算
- 缺点:消耗系统资源高
多进程并发模型--使用fork实现多进程并发(例如之后的ftp服务器)
- 创建套接字,绑定,监听
- 等待接收客户端请求
- 创建新的进程处理客户端请求
- 原有进程继续等待接收新的客户端连接
- 如果客户端退出则关闭子进程
在父进程中忽略子进程状态改变,子进程退出自动由系统处理
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
# fork_server.py
from socket import *
import os
import sys
import signal# 客户端处理函数
def client_handler(c):print("处理子进程的请求:", c.getpeername())try:while True:data = c.recv(1024)if not data:breakprint(data)c.send("收到客户端的请求".encode())except (KeyboardInterrupt, SystemError):sys.exit()except Exception as e:print(e)c.close()sys.exit(0)# 创建套接字
HOST = ""
PORT = 9999
ADDR = (HOST, PORT)s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)print("进程%d等待客户端连接" % os.getpid())
# 在父进程中忽略子进程状态改变,子进程退出自动由系统处理
signal.signal(signal.SIGCHLD, signal.SIG_IGN)while True:try:c,addr = s.accept()except KeyboardInterrupt:sys.exit("服务器推出")except Exception as e:print("Error:", e)continue# 为客户端创建新的进程,处理请求pid = os.fork()# 子进程处理请求if pid == 0:client_handler(c)# 父进程或者创建失败,都会继续等待下个客户端连接else:continue
进程6254等待客户端连接
处理子进程的请求: ('127.0.0.1', 63820)
b'hello'
处理子进程的请求: ('127.0.0.1', 63828)
b'world'An exception has occurred, use %tb to see the full traceback.SystemExit: 服务器推出An exception has occurred, use %tb to see the full traceback.SystemExit: 服务器推出An exception has occurred, use %tb to see the full traceback.SystemExit: 服务器推出/Users/haoen110/miniconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3275: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
/Users/haoen110/miniconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3275: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
/Users/haoen110/miniconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3275: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
#tcp_client.py
from socket import *#创建套接字
sockfd = socket(AF_INET,SOCK_STREAM)#发起连接
server_addr = ('127.0.0.1',9999)
sockfd.connect(server_addr)while True:#消息发送接收data = input("发送>>")if not data:break sockfd.send(data.encode())data = sockfd.recv(1024)print("接受到:",data.decode())#关闭套接字
sockfd.close()
ftp文件服务器
- 项目功能
- 服务端和客户端两部分,要求启动一个服务端,可以同时处理多个客户端请求
- 功能
- 可以查看服务端文件库中所有的普通文件
- 从客户端可以下载文件库的文件到本地
- 可以将本地文件上传的服务端文件库
- 退出
客户端使用print在终端打印简单的命令提示,通过命令提示发起请求
技术分析(fork、tcp并发)
- 每个功能要单独封装,整体功能写在一个类中
- 如何搭建整体架构,完成网络通讯
功能分析
- 获取文件列表
- 客户端:
- 发送请求
- 得到回复判断能否获取列表
- 接收文件名称列表打印
- 服务端:
- 接收请求
- 判断请求类型
- 判断能否满足请求,回复信息确认
- 执行请求发送文件列表
os.listdir(path)
获取目录中文件列表
os.path.isfile()
判断是否为普通文件
os.path.isdir()
判断是否为目录
- 客户端:
文件下载
- 客户端:
- 发送请求 (文件名)
- 得到回复判断能否下载
- 下载文件
- 服务端:
- 接收请求
- 判断请求类型
- 判断能否满足请求,回复信息确认
- 执行请求发送文件
'''
ftp 文件服务器
'''
from socket import *
import os
import sys
import time
import signal #文件库路径
FILE_PATH = "~/Documents/ftpFile/"
HOST = '0.0.0.0'
PORT = 8000
ADDR = (HOST,PORT)#将文件服务器功能写在类中
class FtpServer(object):def __init__(self,connfd):self.connfd = connfddef do_list(self):#获取文件列表file_list = os.listdir(FILE_PATH)if not file_list:self.connfd.send("文件库为空".encode()) return else:self.connfd.send(b'OK')time.sleep(0.1)files = ''for file in file_list:if file[0] != '.' and \os.path.isfile(FILE_PATH + file):files = files + file + '#'self.connfd.sendall(files.encode())def do_get(self,filename):try:fd = open(FILE_PATH + filename,'rb')except:self.connfd.send('文件不存在'.encode())return self.connfd.send(b'OK')time.sleep(0.1)#发送文件while True:data = fd.read(1024)if not data:time.sleep(0.1)self.connfd.send(b'##')breakself.connfd.send(data)print("文件发送完毕")def do_put(self,filename):try:fd = open(FILE_PATH + filename,'wb')except:self.connfd.send('上传失败'.encode())return self.connfd.send(b'OK')while True:data = self.connfd.recv(1024)if data == b'##':breakfd.write(data)fd.close()print("上传完毕")#创建套接字,接收客户端连接,创建新的进程
def main():sockfd = socket()sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)sockfd.bind(ADDR)sockfd.listen(5)#处理子进程退出signal.signal(signal.SIGCHLD,signal.SIG_IGN)print("Listen the port 8000...")while True:try:connfd,addr = sockfd.accept()except KeyboardInterrupt:sockfd.close()sys.exit("服务器退出")except Exception as e:print("服务器异常:",e)continue print("已连接客户端:",addr)#创建子进程pid = os.fork()if pid == 0:sockfd.close()ftp = FtpServer(connfd)#判断客户端请求while True:data = connfd.recv(1024).decode()if not data or data[0] == 'Q':connfd.close()sys.exit("客户端退出")elif data[0] == 'L':ftp.do_list()elif data[0] == 'G':filename = data.split(' ')[-1]ftp.do_get(filename)elif data[0] == 'P':filename = data.split(' ')[-1]ftp.do_put(filename)else:connfd.close()continueif __name__ == "__main__":main()
'''
ftp 客户端
'''
from socket import *
import sys
import time #基本文件操作功能
class FtpClient(object):def __init__(self,sockfd):self.sockfd = sockfd def do_list(self):self.sockfd.send(b'L') #发送请求#等待回复data = self.sockfd.recv(1024).decode()if data == 'OK':data = self.sockfd.recv(4096).decode()files = data.split('#')for file in files:print(file)print("文件列表展示完毕\n")else:#由服务器发送失败原因print(data)def do_get(self,filename):self.sockfd.send(('G ' + filename).encode())data = self.sockfd.recv(1024).decode()if data == 'OK':fd = open(filename,'wb')while True:data = self.sockfd.recv(1024)if data == b'##':breakfd.write(data)fd.close()print("%s 下载完毕\n"%filename)else:print(data)def do_put(self,filename):try:f = open(filename,'rb')except:print("没有找到文件")return self.sockfd.send(('P ' + filename).encode())data = self.sockfd.recv(1024).decode()if data == 'OK':while True:data = f.read(1024)if not data:time.sleep(0.1)self.sockfd.send(b'##')break self.sockfd.send(data)f.close()print("%s 上传完成"%filename)else:print(data)def do_quit(self):self.sockfd.send(b'Q')#网络连接
def main():if len(sys.argv) < 3:print("argv is error")return HOST = sys.argv[1]PORT = int(sys.argv[2])ADDR = (HOST,PORT) #文件服务器地址sockfd = socket()try:sockfd.connect(ADDR)except:print("连接服务器失败")returnftp = FtpClient(sockfd) #功能类对象while True:print("========== 命令选项 ===========")print(">>list")print(">>get file")print(">>put file")print(">>quit")print("===============================")cmd = input("请输入命令>>")if cmd.strip() == 'list':ftp.do_list()elif cmd[:3] == 'get':filename = cmd.split(' ')[-1]ftp.do_get(filename)elif cmd[:3] == 'put':filename = cmd.split(' ')[-1]ftp.do_put(filename)elif cmd.strip() == "quit":ftp.do_quit()sockfd.close()sys.exit("谢谢使用")else:print("请输入正确命令!!!")continueif __name__ == "__main__":main()
threading的多线程并发
对比多进程并发:
* 消耗资源较少
* 线程应该更注意共享资源的操作
* 在python中应该注意GIL问题,网络延迟较高,线程并发也是一种可行的办法
- 创建套接字,绑定监听
- 接收客户端请求,创建新的线程
- 主线程继续接收其他客户端连接
- 分支线程启动对应的函数处理客户端请求
- 当客户端断开,则分支线程结束
import traceback
traceback.print_exc()
功能:更详细的打印异常信息
from socket import *
import os,sys
from threading import *
import traceback HOST = '0.0.0.0'
PORT = 8888
ADDR = (HOST,PORT)#客户端处理函数
def handler(connfd):print("Connect from",connfd.getpeername())while True:data = connfd.recv(1024)if not data:break print(data.decode())connfd.send(b'Receive request')connfd.close()#创建套接字
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ADDR)
s.listen(5)#等待客户端请求
while True:try:connfd,addr = s.accept()except KeyboardInterrupt:s.close()sys.exit("服务器退出")except Exception:traceback.print_exc()continue t = Thread(target = handler,args = (connfd,))t.setDaemon(True)t.start()
集成模块的使用
python2:SocketServer
python3: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
# TCP
from socketserver import * #创建服务器类
# class Server(ForkingMixIn,TCPServer):
# class Server(ForkingTCPServer):
class Server(ThreadingMixIn,TCPServer):pass class Handler(StreamRequestHandler):def handle(self):#self.request ==> accept 返回的套接字print("Connect from",\self.request.getpeername()) while True:data = self.request.recv(1024)if not data:breakprint(data.decode())self.request.send(b'Receive')if __name__ == "__main__":server_addr = ('0.0.0.0',8888)#创建服务器对象server = Server(server_addr,Handler)#启动服务器server.serve_forever()
# UDP
from socketserver import * #创建服务器类
class Server(ThreadingMixIn,UDPServer):pass class Handler(DatagramRequestHandler):def handle(self):while True: data = self.rfile.readline()print(self.client_address)if not data:break print(data.decode())self.wfile.write(b"Receive")if __name__ == "__main__":server_addr = ('0.0.0.0',8888)#创建服务器对象server = Server(server_addr,Handler)#启动服务器server.serve_forever()
HTTPServer V2.0
- 作用
- 接收客户端请求
- 解析客户端请求
- 组织数据,形成HTTP response
- 将数据发送给客户端
- 升级
- 采用多线程并发接收多个客户端请求
- 基本的请求解析,根据请求返回相应的内容
- 除了可以请求静态网页,也可以请求简单的数据
- 将功能封装在一个类中
- 技术点
- socket tcp 套接字
- http协议的请求响应格式
- 线程并发的创建方法
- 类的基本使用
#coding=utf-8
'''
http server v2.0
1.多线程并发
2.可以请求简单数据
3.能进行简单请求解析
4.结构使用类进行封装
'''
from socket import *
from threading import Thread
import sys
import traceback#httpserver类 封装具体的服务器功能
class HTTPServer(object):def __init__(self,server_addr,static_dir):#增添服务器对象属性self.server_address = server_addrself.static_dir = static_dir self.ip = server_addr[0]self.port = server_addr[1]#创建套接字self.create_socket()def create_socket(self):self.sockfd = socket()self.sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)self.sockfd.bind(self.server_address)#设置监听等待客户端连接def serve_forever(self):self.sockfd.listen(5)print("Listen the port %d"%self.port)while True:try:connfd,addr = self.sockfd.accept()except KeyboardInterrupt:self.sockfd.close()sys.exit("服务器退出")except Exception:traceback.print_exc()continue#创建新的线程处理请求clientThread = Thread\(target = self.handleRequest,args=(connfd,))clientThread.setDaemon(True)clientThread.start()#客户端请求函数def handleRequest(self,connfd):#接收客户端请求request = connfd.recv(4096)#解析请求内容requestHeaders = request.splitlines()print(connfd.getpeername(),":",requestHeaders[0])#获取具体请求内容getRequest = str(requestHeaders[0]).split(' ')[1]if getRequest == '/' or getRequest[-5:] == '.html':self.get_html(connfd,getRequest)else:self.get_data(connfd,getRequest)connfd.close()def get_html(self,connfd,getRequest):if getRequest == '/':filename = self.static_dir + "/index.html"else:filename = self.static_dir + getRequesttry:f = open(filename)except Exception:#没有找到页面responseHeaders = "HTTP/1.1 404 NOT FOUND\r\n"responseHeaders += '\r\n'responseBody = "Sorry,not found the page"else:responseHeaders = "HTTP/1.1 200 OK\r\n"responseHeaders += '\r\n'responseBody = f.read()finally:response = responseHeaders + responseBodyconnfd.send(response.encode())def get_data(self,connfd,getRequest):urls = ['/time','/tedu','/python']if getRequest in urls:responseHeaders = "HTTP/1.1 200 OK\r\n"responseHeaders += '\r\n'if getRequest == "/time":import timeresponseBody = time.ctime()elif getRequest == '/tedu':responseBody = "Welcome to tedu"elif getRequest == '/python':responseBody = "人生苦短我用Python"else:responseHeaders = "HTTP/1.1 404 NOT FOUND\r\n"responseHeaders += '\r\n'responseBody = "Sorry,not found the data" response = responseHeaders + responseBodyconnfd.send(response.encode())if __name__ == "__main__":#服务器IPserver_addr = ('0.0.0.0',8000)#我的静态页面存储目录static_dir = './static'#生成对象httpd = HTTPServer(server_addr,static_dir)#启动服务器httpd.serve_forever()
协程基础
定义:纤程,微线程。协程的本质是一个单线程程序,所以协程不能够使用计算机多核资源。
作用:能够高效的完成并发任务,占用较少的资源。因此协程的并发量较高。
原理:通过记录应用层的上下文栈区,实现在运行中进行上下文跳转,达到可以选择性地运行想要运行的部分,以此提高程序的运行效率。
- 优点:
- 消耗资源少
- 无需切换开销
- 无需同步互斥
- IO并发性好
缺点:无法利用计算机多核
yield ---> 协程实现的基本关键字
greenlet
- `greenlet.greenlet()`生成协程对象
- `gr.switch()`选择要执行的协程事件
from greenlet import greenlet def test1():print(12)gr2.switch()print(34)gr2.switch()def test2():print(56)gr1.switch() # 会接着上一次的继续打印print(78)#协程对象
gr1 = greenlet(test1)
gr2 = greenlet(test2)gr1.switch()
12
56
34
78
gevent
- 作用:
- 将协程事件封装为函数
- 生成协程对象
gevent.spawn(func,argv)
- 功能:生成协程对象
- 参数:
- func 协程函数
- argv 给协程函数传参
- 返回值:返回协程对象
gevent.joinall()
- 功能:回收协程
- 参数:列表,将要回收的协程放入列表
gevent.sleep(n)
- 功能:设置协程阻塞,让协程跳转
- 参数:n 阻塞时间
from gevent import monkey
monkey.patch_all()
功能:修改套接字的IO阻塞行为
必须在socket导入之前使用
import gevent
from time import sleep def foo(a,b):print("a = %d,b = %d"%(a,b))gevent.sleep(2)print("Running foo again")def bar():print("Running int bar")gevent.sleep(3)print("Running bar again")#生成协程
f = gevent.spawn(foo,1,2)
g = gevent.spawn(bar)
# sleep(3)
print("===============")
gevent.joinall([f,g])
print("%%%%%%%%%%%%%%")
===============
a = 1,b = 2
Running int bar
Running foo again
Running bar again
%%%%%%%%%%%%%%
import gevent from gevent import monkey
monkey.patch_all()from socket import *
from time import ctime def server(port):s = socket()s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)s.bind(('0.0.0.0',port))s.listen(3)while True:c,addr = s.accept()print("Connect from ",addr)# handler(c) #循环服务器gevent.spawn(handler,c) #协程服务器def handler(c):while True:data = c.recv(1024)if not data:breakprint(data.decode())c.send(ctime().encode())c.close()if __name__ == "__main__":server(8888)
转载于:https://www.cnblogs.com/haoenwei/p/10701015.html
PythonServer相关推荐
- ansible之fetch模块
fetch官方链接http://docs.ansible.com/ansible/fetch_module.html 文件拉取模块主要是将远程主机中的文件拷贝到本机中,和copy模块的作用刚刚相反,并 ...
- 运行python-thrift的DEMO
启动服务器和客户端 cd ~/thrift-0.8.0/ thrift -r --gen py tutorial.thrift #生成代码 cd py ./PythonServer.py #启动服务器 ...
- thrift入门(1) -- 安装配置
thrift 是一个跨语言的通讯框架,支持c++, java, .net, python,php等.你可以用一种语言写一个服务器,然后另外一种语言写一个客户端,快速搭建一个rpc调用服务.而且它很轻量 ...
- 在python中用import或者from_[转]python基础之---import与from...import....
简单说说python import与from...import....(python模块) 在python用import或者from...import来导入相应的模块.模块其实就一些函数和类的集合文件 ...
- Linux学习之后台运行进程
前言 有的时候我们希望Linux的命令行能运行一个程序,然后把它放在后台,让其自己运行,尤其是云服务器运行单线程程序,TCP服务器开启一个while(true)循环监听一个端口,这时命令行就死在那等着 ...
- VII python(1)基础知识
VII Python(1)基础知识 1989年诞生(同LinuxOS),google推崇,06年后才开始在中国流行,08年运维开始使用python www.python.org(主流OS默认配的pyt ...
- nginx配置vhost例子
安利一篇我翻译的国外大牛的神经网络入门文章 nginx下面几个配置vhost的例子 php server { server_name stage.localhost; listen 80 ;root ...
- 局域网共享软件开发python_Python+flask 实现的局域网文件共享服务器
一直没有用python 做过web开发,以前看python的教程的时候,都会跳过这个环节,最近没事的时候,看来一下,感觉还挺方便的.就一边算是学习,一边算是练习,写了一个python实现的局域网文件共 ...
- python访问局域网共享文件夹_Python+flask 实现的局域网文件共享服务器
一直没有用python 做过web开发,以前看python的教程的时候,都会跳过这个环节,最近没事的时候,看来一下,感觉还挺方便的.就一边算是学习,一边算是练习,写了一个python实现的局域网文件共 ...
- ansible——自动化运维工具
目录 ansible 简介 ansible 是什么? ansible 特点 ansible 架构图 ansible 任务执行 ansible 任务执行模式 ansible 执行流程 ansible 命 ...
最新文章
- 转置型FIR滤波器的fpga实现
- html列表穿插广告怎么实现,基于innerHTML中的script广告实现代码[广告全部放在一个js里面]...
- 计算机组成原理(哈工大刘宏伟)135讲(三)
- aspnet网站开发实例_新手用户如何根据网站类型或应用场景选择云服务器?
- Android Studio使用说明
- IDEA 创建 SpringBoot 项目
- 【数学基础】最小二乘法
- 今天来总结一下CSS中有哪些定位
- Google猜画小歌升级:现在可以跟好友对战了
- 下载文件扩展名php,[宜配屋]听图阁
- “卖键盘的被键盘侠喷了” | 社交网络的戾气怎么这么重
- 使用 Java Annotation 定制 Ant Junit Report
- 音阶频率对照表_各个音阶的对应频率
- java 球弹跳的高度_关于皮球弹跳高度的几个问题
- c语言中常用函数名,C语言中一些常用函数的说明
- IOS版本APP STORE上架流程
- Instrument详解
- 这一次,彻底拿下操作系统!!!
- 罗德里格斯公式证明(Games101)
- 【OpenCV】2020年关于SIFT算法专利版权问题的解决办法