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 = "<h1>Sorry,not found the page</h1>"
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()

Python网络编程(socketserver、TFTP云盘、HTTPServer服务器模型)的更多相关文章

  1. python网络编程socketserver模块(实现TCP客户端/服务器)

    摘录python核心编程 socketserver(python3.x版本重新命名)是标准库中的网络编程的高级模块.通过将创建网络客户端和服务器所必须的代码封装起来,简化了模板,为你提供了各种各样的类 ...

  2. Python网络编程基础|百度网盘免费下载|零基础入门学习资料

    百度网盘免费下载:Python网络编程基础|零基础学习资料 提取码:k7a1 目录: 第1部分 底层网络 第1章 客户/服务器网络介绍 第2章 网络客户端 第3章 网络服务器 第4章 域名系统 第5章 ...

  3. python网络编程——SocketServer/Twisted/paramiko模块

    在之前博客C/S架构的网络编程中,IO多路复用是将多个IO操作复用到1个服务端进程中进行处理,即无论有多少个客户端进行连接请求,服务端始终只有1个进程对客户端进行响应,这样的好处是节省了系统开销(se ...

  4. python网络编程--socketserver 和 ftp功能简单说明

    1. socketserver 我们之前写的tcp协议的socket是不是一次只能和一个客户端通信,如果用socketserver可以实现和多个客户端通信.它是在socket的基础上进行了一层封装,也 ...

  5. python网络编程-socketserver

    一:socketserver简化了网络服务器的编写. 它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer. 这4个类是同步进行处 ...

  6. python 网络编程(socketserver,阻塞,其他方法)

    重点回顾: (重点)粘包 : 就是因为接收端不知道如何接收数据,造成接收数据的混乱的问题 只发生在tcp协议上. 因为tcp协议的特点是面向数据流形式的传输 粘包的发生主要是因为tcp协议有两个机制: ...

  7. python网络编程-socketserver模块

    使用socketserver 老规矩,先引入import socketserver 必须创建一个类,且继承socketserver.BaseRequestHandler 这个类中必须重写handle( ...

  8. Python网络编程03 /缓存区、基于TCP的socket循环通信、执行远程命令、socketserver通信

    Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命令.socketserver通信 目录 Python网络编程03 /缓存区.基于TCP的socket循环通信.执行远程命 ...

  9. Python网络编程基础 PDF 完整超清版|网盘链接内附提取码下载|

    点此获取下载地址提取码:y9u5 Python网络编程最好新手入门书籍!175个详细案例,事实胜于雄辩,Sockets.DNS.Web Service.FTP.Email.SMTP.POP.IMAP. ...

  10. Python 网络编程(二)

    Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...

随机推荐

  1. NodeJS学习日记--环境配置及项目初始化

    在node.js官网下载nodejs安装包 安装完成后打开控制台,输入 npm -version 如果正确显示npm版本则安装成功. 创建项目之前先要安装以下全局扩展模块 npm install -g ...

  2. JVM 监控以及内存分析

    1 内存分析1.1 jmap -histo 命令pid=`jps | awk '{if ($2 == "Jps") print $1}'`jmap -histo $pid > ...

  3. Code First 一

    Code-First和我们的数据库优先方式是相反的,数据库优先是通过数据库映射出相应的类和上下文,Code-First测试通过创建的类和上下文得到相应的数据库. Code-First主要用于领域驱动设 ...

  4. 到底什么时候需要使用 final

    final: final修饰属性,则该属性不可再次改变,而且在初始化中必须在属性或者是构造方法中其中且中有一个中初始化他 final修饰方法,则该方法不可被重写 final修饰类,则不可被继承 1:当 ...

  5. o'Reill的SVG精髓(第二版)学习笔记——第八章

    第八章:图案和渐变 要使用图案,首先要定义一个水平或者垂直方向重复的图形对象,然后用它填充另一个对象或者作为笔画使用.这个图形对象呗称作tile(瓷砖). 下面可以把SVG绘制的二次曲线作为图案. & ...

  6. 数据库——MySQL——存储引擎

    现实生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处理文本用txt类型,处理表格用excel,处理图片用png等.数据库中的表也应该有不同的类型,表的类型不同,会对 ...

  7. 取消Eclipse的自动代码格式化

    前段时间在Eclipse里面设置了java文件保存时自动格式化,在java->Code Style->Formatter里设置了自定义的格式化的样式,这样每次保存后都会自动格式化代码,用了 ...

  8. java程序执行命令行,解锁数据库表

    有些表锁的时间长或其他原因,在plsql中不能解锁,只能用命令行解锁. 有些功能跨平台系统的交互偶尔会锁表,就需要自动解锁. 下面是解锁的代码: package com.lg.BreakOracleU ...

  9. webBrowser 应用编程函数总结

    /*============================说明部分================================= 实现一下函数需包含头文件 #include <Winine ...

  10. ECSHOP和SHOPEX快递单号查询顺丰插件V8.6专版

    发布ECSHOP说明: ECSHOP快递物流单号查询插件特色 本ECSHOP快递物流单号跟踪插件提供国内外近2000家快递物流订单单号查询服务例如申通快递.顺丰快递.圆通快递.EMS快递.汇通快递.宅 ...