python服务器云盘_Python网络编程(socketserver、TFTP云盘、HTTPServer服务器模型)

HTTP?

HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。

通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS

默认HTTP的端口号为80,HTTPS的端口号为443。     what?  无状态什么鬼?        HTTP无状态协议是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,         则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快         由于web等客户端与服务器交互的应用程序出现后HTTP的无状态严重阻碍了这些应用的实现效率 说以就产生了cookie和Session cookie: 当用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;

接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体(Response Body)中的, 而是存放于HTTP响应头(Response Header);当客户端浏览器接收到来自服务器的响应之后, 浏览器会将这些信息存放在一个统一的位置,对于Windows操作系统而言, 我们可以从: [系统盘]:\Documents and Settings\[用户名]\Cookies目录中找到存储的Cookie;自此,客户端再向服务器发送请求的时候, 都会把相应的Cookie再次发回至服务器。而这次,Cookie信息则存放在HTTP请求头(Request

Header)了。 Session : 所谓session就是指客户端与服务端之间的一种交互过程的状态信息(数据) 这个状态的定界以及生命期是应用本身的事情 当一个用户向服务器发送第一个请求时,服务器为其建立一个session 并且会给这个session创建一个标识号 这个用户随后的请求都应该包括这个标识好服务器会对这个标识判断请求属于哪一个session 这种机制不使用IP作为标识,因为很多机器是代理服务器上网,无法区分主机 可以用cookie和URL重写来实现session标识号(sessionID)

URL只是一个统称 实际上是URI包含URL和URN由于URN用的非常少 几乎说有的URI都是URL所以人们更喜欢叫URL

os.

listdir(path)           获取文件列表

os.path.

isfile() :             判断一个 文件是否为普通文件 os.path.

isdir() :            判断一个文件是否为目录 TFTP 文件服务器

项目功能 :

* 客户端有简单的页面命令提示     * 功能包含:                1. 查看服务器文件库中的文件列表(普通文件)

2. 可以下载其中的某个文件到本地                3. 可以上传客户端文件到服务器文件库

* 服务器需求 :                1. 允许多个客户端同时操作

2.每个客户端可能回连续发送命令

技术分析:        1. tcp套接字更适合文件传输        2. 并发方案  ---》 fork 多进程并发        3. 对文件的读写操作        4.

获取文件列表 ----》 os.listdir()         粘包的处理

整体结构设计        1. 服务器功能封装在类中(上传,下载,查看列表)        2. 创建套接字,流程函数调用  main()        3. 客户端负责发起请求,接受回复,展示

服务端负责接受请求,逻辑处理

编程实现        1. 搭建整体结构,创建网络连接        2. 创建多进程和类的结构        3. 每个功能模块的实现

服务器端:

from socket import *

import os

import signal

import sys

import time

# 文件库

FILE_PATH = "/home/tarena/"

# 实现功能模块

class TftpServer(object):

def __init__(self,connfd):

self.connfd = connfd

# 查询

def 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 os.path.isfile(FILE_PATH+file) and file[0] != '.':

# 文件名间隔符 用于客户端解析

files = files + file + '#'

# 一次全部发送 简单粗暴

self.connfd.send(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)

# 发送文件

try:

while True:

data = fd.read(1024)

if not data:

break

self.connfd.send(data)

except Exception as e:

print(e)

time.sleep(0.1)

self.connfd.send(b'##') # 表示文件发送完成

print("文件发送完毕")

# 上传

def do_put(self,filename):

# 限制文件命重复导致覆盖源文件

try:

fd = open(FILE_PATH+filename,'xb')

except:

self.connfd.send("无法上传".encode())

return

except FileExistsError:

self.connfd.send("文件已存在".encode())

return

self.connfd.send(b'OK')

# 上传文件

while True:

data = self.connfd.recv(1024)

if data == b'##':

break

fd.write(data)

fd.close()

print("文件上传完毕")

# 流程控制,创建套接字,创建并发,方法调用

def main():

HOST = '0.0.0.0'

PORT = 8888

ADDR = (HOST,PORT)

# 创建套接字

sockfd = socket()

sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

sockfd.bind(ADDR)

sockfd.listen(5)

# 忽略子进程退出

signal.signal(signal.SIGCHLD, signal.SIG_IGN)

# 循环等待客户端链接

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()

tftp = TftpServer(connfd) # __init__传参

while True:

data = connfd.recv(1024).decode()

# 断开连接

if (not data) or data[0] == 'Q':

print("客户端退出")

sys.exit(0)

elif data[0] == "L":

# 申请查询

tftp.do_list()

elif data[0] == 'G':

# 解析文件名

filename = data.split(' ')[-1]

# 申请下载

tftp.do_get(filename)

elif data[0] == 'P':

filename = data.split(' ')[-1]

# 申请上传

tftp.do_put(filename)

else:

print("客户端发送错误指令")

else:

# 关闭父进程内无用套接字

connfd.close()

# 父进程只用来做客户端链接

continue

if __name__ == "__main__":

main()

复制代码

客户端:

from socket import *

import sys

import time

# 实现各种功能请求

class TftpClient(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("文件展示完毕")

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'##':

break

fd.write(data)

fd.close()

print("%s 下载完成\n"%filename)

else:

# 请求失败原因

print(data)

def do_put(self,filename):

# 判断本地是否有要上传的文件

try:

fd = open(filename,'rb')

except:

print("上传文件不存在")

return

self.sockfd.send(("P "+filename).encode())

data = self.sockfd.recv(1024).decode()

# 请求成功

if data == 'OK':

while True:

data = fd.read(1024)

if not data:

break

self.sockfd.send(data)

fd.close()

# 发送结束符并防止粘包

time.sleep(0.1)

self.sockfd.send(b'##')

print("%s 上传完毕"%filename)

else:

# 请求失败原因

print(data)

# 创建套接字并建立连接

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()

sockfd.connect(ADDR)

# 创建对象

tftp = TftpClient(sockfd)

while True:

print("")

print("==========命令选项===========")

print("********** list *********")

print("********** get file ******")

print("********** put file ******")

print("********** quit *********")

print("=============================")

cmd = input("输入命令>>")

# 去除空格判断命令

if cmd.strip() == "list":

# 查询

tftp.do_list()

# 获取文件上传或下载命令

elif cmd[:3] == "get":

# 拆分命令获取文件名

filename = cmd.split(' ')[-1]

# 下载

tftp.do_get(filename)

elif cmd[:3] == "put":

filename = cmd.split(' ')[-1]

# 上传

tftp.do_put(filename)

# 退出

elif cmd.strip() == "quit":

sockfd.send(b'Q')

sockfd.close()

sys.exit("欢迎使用")

else:

print("请输入正确命令!")

if __name__ == "__main__":

main()复制代码

多线程并发

threading模块完成多线程并发

对比多进程并发      优势 :             资源消耗少      缺点 :              需要注意对共享资源的操作

实现步骤:        1. 创建套接字,绑定,监听

2. 接收客户端连接请求,创建新的线程        3. 主线程继续等待其他客户端连接,分支线程执行客户端具体请求        4. 处理完客户端请求后分支线程自然退出,关闭客户端套接字 示例:

from socket import *

import os,sys

from threading import *

HOST = "0.0.0.0"

PORT = 8888

ADDR = (HOST,PORT)

#客户端处理函数

def handler(connfd):

print("Connect from",connfd.getpeername())

while True:

data = connfd.recv(1024).decode()

if not data:

break

print(data)

connfd.send(b'Receive your msg')

connfd.close()

def main(ADDR):

s = socket()

s.bind(ADDR)

s.listen(5)

while True:

try:

connfd,addr = s.accept()

# 处理 Ctrl + C

except KeyboardInterrupt:

s.close()

sys.exit("服务器退出")

# 其他异常

except Exception as e:

print(e)

continue

# 创建子线程用于处理客户端请求

t = Thread(target=handler,args= (connfd,))

t.setDaemon(True)

t.start()

if __name__ == __main__:

main()

复制代码

socket并发集成模块

python2  SocketServer python3

socketserver

功能 :                  通过模块提供的接口组合可以完成多进程/多线程  tcp/udp的         并发程序

StreamRequestHandler      处理tcp请求 DatagramRequestHandler  处理udp请求

ForkingMixIn      创建多进程 ThreadingMixIn  创建多线程   TCPServer  创建tcp  server UDPServer 创建udp

server

ForkingTCPServer       等于    ForkingMixIn  +  TCPServer  ForkingUDPServer     等于    ForkingMixIn  +  UDPServer  ThreadingTCPServer  等于   ThreadingMixIn  +  TCPServer  ThreadingUDPServer  等于   ThreadingMixIn  +  UDPServer

示例:

#多进程 tcp server

from socketserver import *

#创建server类

# class Server(ForkingMixIn,TCPServer):

# class Server(ForkingTCPServer):

# pass

#多线程tcp并发

class Server(ThreadingTCPServer):

pass

#具体的请求处理类

class Handler(StreamRequestHandler):

def handle(self):

# self.request ==> accept返回的套接字

print("Connect from",self.request.getpeername())

while True:

data = self.request.recv(1024).decode()

if not data:

break

print(data)

self.request.send(b'Receive')

if __name__ == __main__:

#创建server对象

server = Server(("0.0.0.0",8888),Handler)

#启动服务器

server.serve_forever()

复制代码

基于多线程并发的HTTPServer

1. 接收浏览器http请求        2. 对请求进行一定的解析        3. 根据解析结果返回对应内容        4. 如果没有请求内容则返回404        5. 组织Response格式进行回发

升级:        * 使用多线程并发        * 增加了具体的请求解析和404情况        * 使用类进行代码封装        * 增加一定的数据获取功能

技术点 : threading并发

tcp socket 传输         HTTP请求和响应格式

相比上次升级了一点点

from socket import *

from threading import Thread

import time

# 存放静态页面的目录

STATIC_DIR = "./static"

ADDR = ('0.0.0.0', 8000)

# HTTPServer类,封装具体功能

class HTTPServer(object):

def __init__(self, address):

# 创建套接字

self.sockfd = socket()

# 设置端口重用

self.sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

self.sockfd.bind(address)

self.sockfd.listen(5)

# 为对象增加属性变量

self.name = "HTTPServer"

self.port = address[1]

self.address = address

# 启动服务器

def serve_forever(self):

print("Listen the port %d"%self.port)

while True:

# 循环接收客户端请求并创建新的套接字

connfd, addr = self.sockfd.accept()

# 创建线程并运行处理具体请求

clientThread = Thread(target = self.handleRequest,args = (connfd,))

# 主线程结束时结束线程

clientThread.setDaemon(True)

clientThread.start()

def handleRequest(self, connfd):

# 接收客户端请求

request = connfd.recv(4096)

# 按行切割 字符串

requestHeadlers = request.splitlines()

# 获取请求行

print(connfd.getpeername(), ":" , requestHeadlers[0])

# 获取请求内容并解析

getRequest = str(requestHeadlers[0]).split(' ')[1]

# 并判断请求类型

if getRequest == '/' or getRequest[-5:] == '.html':

# 请求行为网页请求

data = self.get_html(getRequest)

else:

# 请求指定数据内容

data = self.get_data(getRequest)

# 响应请求并返还内容

connfd.send(data.encode())

connfd.close()

# 用于处理网页请求

def get_html(self,page):

# 判断是否为主页请求

if page == "/":

filename = STATIC_DIR + "/index.html"

else:

filename = STATIC_DIR + page

try:

f = open(filename)

except Exception:

# 没有找到页面

responseHeadlers = "HTTP/1.1 404 Not Found\r\n"

responseHeadlers += "Content-Type: text/html\r\n"

responseHeadlers += '\r\n'

responseBody = "

Sorry,not found the page

"

else:

responseHeadlers = "HTTP/1.1 200 OK\r\n"

responseHeadlers += "Content-Type: text/html\r\n"

responseHeadlers += '\r\n'

for i in f:

responseBody += i

# 页面存不存在否响应

finally:

return responseHeadlers + responseBody

# 用于处理数据内容请求

def get_data(self,data):

responseHeadlers = "HTTP/1.1 200 OK\r\n"

responseHeadlers += "\r\n"

if data == "/time":

responseBody = time.ctime()

elif data == "/ParisGabriel":

responseBody = "Welcome to ParisGabriel"

else:

responseBody = "The data not found"

return responseHeadlers + responseBody

if __name__ == "__main__":

# 生成服务器对象

httpd = HTTPServer(ADDR)

# 启动服务器

httpd.serve_forever()复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值