HTTP请求: request
- 请求格式:
- 1、请求行 GET (请求种类) / (请求内容) HTTP/1.1(协议版本)
- 请求种类
- GET 获取网络资源
- POST 提交一定的附加数据,得到返回结果。
- HEAD 获取响应头
- PUT 更新服务器资源
- DELETE 删除服务器资源
- CONNECT 预留
- TRACE 测试
- OPTIONS 获取服务器性能
- 2、请求头 对请求内容的具体描述,系统会自动填充。
- 以键值对的形式对请求信息进行描述
- Accept-Language:zh-CN,zh;q=0.9
- 3、空行
- 4、请求体 提交具体的请求参数
HTTP响应:Response
响应格式:
响应行 反馈具体的相应情况
HTTP/1.1 200 OK
版本信息 响应码 附加信息
响应码: 1xx 提示信息 表示请求已经接受
2xx 相应成功
3xx 响应需要重新请定向
4xx 客户端错误
5xx 服务器错误
常见响应码: 200 成功 404 请求页面不存在 401 没有访问权限 500 服务器发生位置错误 503 服务器暂时无法执行
响应头 对相应信息的具体描述
eg:Cache-Control :private
空行
响应体 将客户想要的内容进行返回
基础的http服务流程程序:
1、接受http请求
2、给出一定的响应
from socket import *
#处理请求,返回响应
def handleClient(connfd):
print("Connect from",connfd.getpeername())
request = connfd.recv(4096)
print(request)
#进行分行
requestHeadLers = request.splitlines()
for i in requestHeadLers:
print(i)
#无论什么请求给出响应的响应。
try:
print("========")
f = open("index.html")
except IOError:
response = "HTTP/1.1 404 not found\r\n"
response += '\r\n'
response += '==Sorry,The page not foundsdfa=='
else:
response = 'HTTP/1.1 200 OK\r\n'
response += '\r\n'
for i in f:
response += i
finally:
connfd.send(response.encode())
connfd.close()
#网络连接控制流程
def main():
print("进入")
sockfd = socket()
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sockfd.bind(("0.0.0.0",8000))
sockfd.listen(5)
print("---")
while True:
print("Listen to the port 80000...")
connfd,addr = sockfd.accept()
handleClient(connfd)
if __name__ == "__main__":
main()
IO: input output
凡是在内存中存在数据交换的操作都可以认为是IO操作
比如:内存和磁盘交互 读写read write
内存和终端交互 print input
内存和网路交互 recv send
IO密集型程序:程序的执行过程中,进行大量的IO操作,而只有较少的cpu运算。消耗计算机资源较少,运行时间长。
cpu密集型程序(计算密集型):程序运行中需要大量的cpu运算,IO操作较少。消耗cpu资源多,运行速度快。
IO分类:
阻塞IO 非阻塞IO IO多路复用 事件IO 异步IO
阻塞IO:默认形态 效率很低的一种IO,简单。
阻塞情况: * 因为某种条件没有达到造成的阻塞。 eg:input accept recv
* 处理IO事件的时间消耗较长带来阻塞 eg:文件的读写过程,网络数据发送过程
非阻塞IO: * 通过修改IO事件的属性,使其变为非阻塞状态,即避免条件阻塞的情况
*非阻塞IO往往和循环搭配使用,这样可以不断执行部分需要执行的代码,也不影响对阻塞条件的判断。
设置套接字为非阻塞:
s.setblocking()
功能:设置套接字的阻塞状态
参数:设置为False则套接字调用函数为非阻塞。
#非阻塞程序
from socket import *
from time import sleep,ctime
s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置s为非阻塞
s.setblocking(False)
#recv变成非阻塞的方法
#connfd.setblocking(False)
while True:
print("Waiting for connect...")
try:
connfd,addr = s.accept()
except BlockingIOError:
sleep(2)
print(ctime())
continue
print("Connect from",addr)
while True:
data = connfd.recv(1024).decode()
if not data:
break
print(data)
connfd.sendall(ctime().encode())
connfd.close
s.close
超时检测:
将原本阻塞的IO设置一个最长阻塞等待事件,在规定事件内如果达到条件则正常执行,如果事件到仍未达到条件则结束阻塞。
s.settimeout(sec)
功能:设置套接字超时时间
参数:设置的时间
#超时等待 settimeout(5)
from socket import *
from time import sleep,ctime
s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置超时等待时间
s.settimeout(5)
while True:
print("Waiting for connect...")
try:
connfd,addr = s.accept()
except timeout:
print("time out")
sleep(2)
print(ctime())
continue
print("Connect from",addr)
while True:
data = connfd.recv(1024).decode()
if not data:
break
print(data)
connfd.sendall(ctime().encode())
connfd.close
s.close
IO多路复用
定义:同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。此时形成多个IO事件都可以操作的现象,不必做个等待执行。
准备就绪:IO事件即将发生的临界状态
import select
select --》windows Linux unix
poll --》 linux unix
epoll --》linux unix
rlist,wlist,xlist = select(rlist,wlist,xlist[,timeout])
功能:监控IO事件,阻塞等待IO事件发生。
参数:rlist 列表 要监控的读IO事件 存放被动等待处理的IO事件
wlist 列表 要监听的写 IO 事件 存放需要主动处理的IO
xlist 列表 要监控的出错IO事件 存入如果发生异常需要处理的IO
timeout 超时时间
返回值: rlist 列表 rlist中准备就绪的IO
wlist 列表 wlist中准备就绪的IO
xlist 列表 xlist中准备就绪的IO
注意事项:
1、IO多路复用处理IO的过程中不应有死循环出席那,使一个客户端长期占有服务端。
2、IO多路复用是一种并发行为,但是是单进程程序,效率较高。
from socket import *
from select import select
s = socket()
#创建tcp套接字作为关注的IO
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(("127.0.0.1",8888))
s.listen(5)
#三个关注列表
rlist = [s]
wlist = []
xlist = []
while True:
print("waitting for .....")
rs,ws,xs = select(rlist,wlist,xlist)
print("Connect from")
for r in rs:
if r is s:
connfd,addr = r.accept()
print("Connect from",connfd)
#增加关注事件
rlist.append(connfd)
else:
data = r.recv(1024).decode()
if not data:
rlist.remove(r)
r.close()
else:
print("receive:",data)
wlist.append(r)
#r.send(b"Receive your message")
for w in ws:
w.send("这是一条回复消息".encode())
#处理异常IO
for x in xs:
if x is s:
s.close()
sys.exit(0)
f = open()
f.flush() 清空缓冲区。
import select
import socket
import sys
sockfd = socket.socket()
sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sockfd.bind(("0.0.0.0",8888))
sockfd.listen(5)
#三个关注列表
rlist = [sockfd,sys.stdin]
wlist = []
xlist = []
f = open("test.txt","w")
while True:
#提交关注的IO事件,等待处理
print("Waiting for IO")
rs,ws,xs = select(rlist,wlist,xlist)
for r in rs:
if r is rs:
c,addr = r.accept()
rlist.append(c)
elif r is sys.stdin:
data = r.readline()
f.write(data)
else:
data = r.recv(1024)
if not data:
rlist.remove(r)
else:
f.write(data.decode()+"\n")
poll(IO多路复用)
from select import poll
1、创建poll对象
p = poll()
2、添加关注的IO
poll IO事件分类
POLLIN POLLOUT POLLERR POLHUP POLLDRI POLLVAL
rlist wlist xlist 断开连接 紧急处理 无效
p.register(s,POLLIN | POLLERR) 关注io
p.unregister(s) 取消对IO的关注
3、进行监控
events = p.poll()
功能:监控关注的IO,阻塞等待IO发生。
返回值:events是一个列表,列表中每个元素为一个元组,代表准备就绪需要处理的IO
events --》[( fileno, event),(),()]
就绪IO的fileno 哪个事件就绪
因为要获取IO对象调用函数 ---》通过fileno得到对象
实施方法:建立比照字典 {s.fileno() : s}
from socket import *
from select import *
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)
#创建poll对象
p = pull()
#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}
#注册关注IO事件
p.register(s,PULLIN|POLLERR)
while True:
#监控关注的IO
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#注册新的关注IO
p.register(c,POLLIN)
#维护字典
fdmap[c.fileno()] = c
elif event & POLLIN:
data = fdmap[fd].recv(1024)
if not data:
#客户端推出,取消关注。
p.unregister(fd)
fdmap[fd].close
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send("收到了".encode())
4、处理IO
epoll 方法:
使用方法: 与poll基本相同
from socket import *
from select import *
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)
#创建epoll对象
p = epull()
#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}
#注册关注IO事件
p.register(s,EPULLIN|EPOLLERR)
while True:
#监控关注的IO
events = p.poll()
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#注册新的关注IO
p.register(c,POLLIN)
#维护字典
fdmap[c.fileno()] = c
elif event & POLLIN:
data = fdmap[fd].recv(1024)
if not data:
#客户端推出,取消关注。
p.unregister(fd)
fdmap[fd].close
del fdmap[fd]
else:
print(data.decode())
fdmap[fd].send("收到了".encode())
注意:生成对象使用epoll() 而不是poll()
register注册IO事件事件类型改为EPOLL事件类型。
select poll epoll区别
1、select可以很好支持windows
2、epoll比select和poll效率高,select和poll差不多
3、epoll提供了更多的触发方式
本地套接字:
linux下文件类型:
b 块是设备文件
c 字符设备文件
d 目录
- 普通文件
l 连接
s 套接字
p 管道
s 作用:用于本地不同程序间进行通信。开辟一个内存,两个程序通过内训通信。
本地套接字创建流程:
1、创建套接字对象
sockfd = socket(AF_UNIX,SOCK_STREAM)
2、绑定本地套接字文件
sockfd.bind(path)
path:一个文件
3、监听
4、接受发送消息
os.path.exists(path)
功能:判断一个文件夹下是否有某个文件
返回值: True False
os.unlink(path) os.remove(path)
功能:删除一个文件
from socket import *
import os
#linux下:使用哪个文件作为套接字文件
sock_file = "./sock"
#判断文件是否已经存在
if os.path.exists(sock_file):
os.unlink(sock_file)
#创建本体套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#绑定
sockfd.bind(sock_file)
#监听
sockfd.listen(5)
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.close()
sockfd.close()
from socket import *
#确保通信两端用相同的套接字文件
sock_file = "./sock"
#创建套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#连接
sockfd.connect(sock_file)
while True:
msg = input("Msg>>")
if msg:
sockfd.send(msg.encode())
else:
break
sockfd.close()