TCP应用之 httpserver
1、接收http请求
2、查看http请求
3、返回一个网页给客户端
1 from socket import * 2 # 接收请求 3 # 查看请求 4 # 返回一个网页给客户端 5 def handleClient(connfd): # 处理浏览器发来的请求 6 request = connfd.recv(4096) 7 # print("***********") 8 # print(request) 9 # print("************") 10 # 按照行切割请求 11 request_lines = request.splitlines() # 去掉换行符 返回列表 12 for line in request_lines: 13 print(line.decode()) 14 15 try: 16 f = open("index1.html",'r') 17 except IOError: 18 response = "HTTP/1.1 303 Not Found\r\n" 19 request += "\r\n" # 空行 20 request += """ 21 ***************************** 22 sorry,not found the page. 23 ***************************** 24 """ 25 else: 26 response = "HTTP/1.1 200 OK\r\n" 27 response += '\r\n' 28 response += f.read() 29 finally: 30 # 发送给浏览器 31 connfd.send(response.encode()) 32 33 def main(): # 创建套接字,调用handleClient完成功能 34 sockfd = socket() # 创建TCP套接字 35 sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 设置端口可重用 36 sockfd.bind(("0.0.0.0",8888)) 37 sockfd.listen(5) # 设置为可监听 没有5会怎样 38 while True: 39 print("监听端口8888") 40 connfd,addr = sockfd.accept() # 等待浏览器连接服务器 41 # 处理浏览器发来的请求 42 handleClient(connfd) # 处理浏览器发来的请求 43 connfd.close() 44 45 if __name__ == "__main__": 46 main()
在浏览器输入地址:127.0.0.1:8888,即可得到网页显示!
IO操作
定义:在内存中数据交换的操作被定义为IO操作,IO------输入输出
内存和磁盘进行数据交换: 文件的读写 数据库更新
内存和终端数据交换 : input print
sys.stdin sys.stdout sys.stderr
内存和网络数据的交换: 网络连接 recv send recvfrom
IO密集型程序 : 程序执行中有大量的IO操作,而较少的cpu运算操作。消耗cpu较少,IO运行时间长
CPU(计算)密集型程序:程序中存在大量的cpu运算,IO操作相对较少,消耗cpu大。
IO分类
IO分为:阻塞IO、非阻塞IO、IO多路复用、事件驱动IO、异步IO
阻塞IO : 程序运行中遇到IO条件没有达成或者传输过程较慢的情况会出现阻塞状态
阻塞IO是IO最简单的逻辑情形,也是默认状态
阻塞IO是效率很低的IO形态
阻塞情况 :
因为某种IO条件没有达到形成阻塞
accept recv recvfrom input
处理IO事件的时候耗时较长形成阻塞:文件读写过程、网络数据发送过程
#将IO设置为非阻塞
s.setblocking(False)
while True:
print("Waiting for connect...")
try:
c,addr = s.accept()
except BlockingIOError:
time.sleep(2)
print(time.ctime())
continue
print("Connect from",addr)
# c.setblocking(False)
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.close()
s.close()
非阻塞IO : 在程序运行中遇到IO的情形不让其产生阻塞
实现手段: 改变IO事件的属性,使其变为非阻塞 s.setblocking(False)
通常会和循环一起使用,尽心条件的循环监控
功能 : 将套接字设置为非阻塞
参数 : 默认为阻塞状态 参数设置为False设置为非阻塞IO
超时检测:将原本阻塞的函数,设置一个阻塞的最长时间。在时间内如果仍然阻塞则不再等待
s.settimeout(sec)
功能 : 设置套接字的超时时间
参数 : 时间 (秒)
from socket import *
import time
s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置套接字的超时检测
s.settimeout(3)
while True:
print("Waiting from connect...")
try:
connfd,addr = s.accept()
except timeout:
print("time out....")
continue
print("Connect from",addr)
while True:
data = connfd.recv(1024).decode()
if not data:
break
print(data)
connfd.close()
s.close()
IO多路复用
定义 :通过一个监测,可以同时监控多个IO事件的行为。当哪个IO事件可以执行,即让这个IO事件发生。
IO事件就绪: IO事件即将发生的一个临界状态
1、在程序中存在的IO事件中选择要监测的
2、创建监测,将监测的IO事件注册
3、等待监测的IO时间发生,判断是什么IO事件
4、处理相应的IO
import select
select ---》 windows linux unix
poll ---》 linux unix
epoll ---》 linux unix
rs, ws, xs = select(rlist, wlist, xlist[, timeout])
功能 : 监控IO事件,阻塞等待监控的IO时间发生
参数 :
rlist是一个列表,存放我们需要(被动)等待处理的IO (接收)
wlist是一个列表,存放我们想主动处理的IO(发送)
xlist是一个列表,存放出错,希望去处理的IO(异常)
timeout 超时检测
返回值:
rs是一个列表,rlist中准备就绪的IO
ws是一个列表,wlist中准备就绪的IO
xs是一个列表,xlist中准备就绪的IO
* 在处理IO时不要形成死循环,让一个客户端单独占有服务端
* IO多路复兴形成一种可以同时处理多个IO的效果,效率较高
from socket import *
from select import select
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)
rlist = [s]
wlist = []
xlist = [s]
while True:
print("等待IO发生")
rs,ws,xs = select(rlist,wlist,xlist)
for r in rs:
if r is s:
connfd,addr = r.accept()
print("Connect from",addr)
rlist.append(connfd)
#表示客户端连接套接字准备就绪
else:
data = r.recv(1024)
if not data:
#从关注列表移除
rlist.remove(r)
r.close()
else:
print("Receive:",data.decode())
#讲客户端套接字放入wlist
wlist.append(r)
for w in ws:
w.send("这是一条回复消息".encode())
wlist.remove(w)
for x in xs:
if x is s:
s.close()
位运算:按照二进制位进行运算符操作
& 按位与 | 按位或 ^ 按位异或 << 左移 >> 右移
11 1011 14 1110
(11 & 14 1010) (11 | 14 1111) (11 ^ 14 0101 )
11 << 2 ===> 44 右侧补0 14 >> 2 ===> 3 挤掉右侧的数字
使用 : 1. 在做底层硬件时操作寄存器
2. 做标志位的过滤
poll方法实现IO多路复用
1. 创建poll对象
p = select.poll()
2. 注册关注的IO
p.register(s,POLLIN | POLLERR)
p.unregister(s) 取消IO关注
事件类别
POLLIN POLLOUT POLLERR POLLHUP POLLPRI
rlist wlist xlist 断开 紧急处理
3. 监控IO
events = p.poll()
功能: 监控关注的IO事件
返回值: 返回发生的IO事件
events 是一个列表 [(fileno,evnet),(),()....]
每个就绪IO对应列表中一个元组:(描述符,就绪事件)
IO地图 : {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 = poll()
#创建地图
fdmap = {s.fileno():s}
#添加关注
p.register(s,POLLIN | POLLERR)
while True:
#进行IO监控
#[(fileno,evnet),...]
events = p.poll()
for fd,event in events:
if fd == s.fileno():
#从地图中找到fd对应的对象
c,addr = fdmap[fd].accept()
print("Connect from",addr)
#注册新的IO 维护地图
p.register(c,POLLIN)
fdmap[c.fileno()] = c
else:
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事件