2018.8.8
目录
day02回顾
tcp套接字
- 网络缓存区
- 粘包
udp 套接字
- 服务端 socket(AF_INET,SOCK_DGRAM)
bind()
recvfrom() sendto() - 客户端 socket recvfrom sendto
tcp编程和udp编程的区别
套接字属性
fileno() getpeername() setsockopt()
广播:一端发送多端接受---》udp
http协议
请求类型:GET POST PUT HEAD DELETE
格式: GET / HTTP/1.1
响应类型:2xx 4xx 5xx
格式: HTTP/1.1 200 OK
day03
tcp应用之 httpserver
- 接收http请求
- 查看http请求
- 返回一个网页给客户端
from socket import *
# 接收请求
# 查看请求
# 返回客户端段请求内容
def handleClient(connfd):
request = connfd.recv(4096)
# print("***********")
# print(request)
# print("************")
#按照行切割请求
request_lines = request.splitlines()
for line in request_lines:
print(line.decode())
try:
f = open('index.html')
except IOError:
response = "HTTP/1.1 303 Not Found\r\n"
response += "\r\n" #空行
response += '''
**************************
Sorry, not found the page.
**************************
'''
else:
response = "HTTP/1.1 200 OK\r\n"
response += '\r\n'
response += f.read()
finally:
#发送给浏览器
connfd.send(response.encode())
#创建套接字,调用handleClient完成功能
def main():
#创建tcp套接字
sockfd = socket()
sockfd.setsockopt\
(SOL_SOCKET,SO_REUSEADDR,1)
sockfd.bind(('0.0.0.0',8000))
sockfd.listen()
while True:
print("Listen the port 8000...")
connfd,addr = sockfd.accept()
#处理浏览器发来的请求
handleClient(connfd)
connfd.close()
if __name__ == "__main__":
main()
#index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tedu</title>
</head>
<body>
<h1>Welcome to Tedu</h1>
</body>
</html>
#网页输入127.0.0.1:8023
#就会返回一个值,返回的是:
Welcome to Tedu
IO
IO input output
- 在内存中存在数据交换的操作可以认为是IO操作(输入输出)
- e.g.:
内存和磁盘进行数据交换: 文件的读写 数据库更新
内存和终端数据交换:input print sys.stdin sys.stdout sys.stdeer
内存和网络数据的交换:网络连接 recv send recvfrom
由IO划分的程序:
- IO密集型程序:程序执行中有大量的IO操作,而较少的cpu运算操作,消费cpu较少,IO运行时间长
- cpu(计算)密集型程序:程序中存在大量的cpu运算,IO操作相对较少,消耗cpu大
IO分类
种类:阻塞IO 非阻塞IO IO多路复用 事件驱动IO 异步IO
- 阻塞IO:程序运行中遇到IO条件没有达成或者传输过从较慢的情况会出现阻塞状态
阻塞IO是 IO最简单的逻辑情形,也是默认状态
阻塞IO是效率很低的IO形态
阻塞情况:因为某种IO条件没有达到形成阻塞
e.g.:accept recv recvfrom input* 处理IO事件的时候耗时比较长形成阻塞 e.g. 文件的读写过程,网络数据发送过程
- 非阻塞IO:在程序运行中遇到IO的情形不让其产生阻塞
实现手段:改变IO事件的属性,使其变为非阻塞
非阻塞状态通常会和循环一起使用,进行条件的循环监控
s.setblocking()
功能 : 将套接字设置为非阻塞状态
参数 :bool 设置为False则表示设置为非阻塞#zusai.py from socket import * import time s = socket() s.bind(('127.0.0.1',8188)) s.listen(5) #将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() #telnet 127.0.0.1 8188
超时检测
将原本阻塞的函数,设置一个阻塞的最长时间,在规定时间内如果仍按阻塞则不再等待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事件就绪:IO事件即将发生的一个临界的状态
1,在程序中存在的IO事件中选择要监测的
2,创建监测,将监测的IO事件注册
3,等待监测的IO事件发生,判断是什么IO事件
4,处理响应的IOimport select
(创建IO多路复用的方法)select —》 windows linux unix
poll —》 linux unix
epoll –》 linux unixrs, 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
注意事项:
1.在处理IO过程中不应该发生死循环(某个IO单独占有服务器)
2.IO多路复用行了一种并发的效果,效率较高
小示例:#sss1.py 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()
这个是day01后面的客户端:(也可以用telnet ip port)
#sss.py from socket import * #创建套接字 sockfd = socket() #发起连接 sockfd.connect(('127.0.0.1',9868)) while True: #消息收发 msg = input("Msg>>") if not msg: break sockfd.sendall(msg.encode()) data = sockfd.recv(1024) print(data.decode()) sockfd.close()
展示:
位运算
1,按照二进制位进行运算操作
&(按位与) | (按位或) ^(按位异或)
<<(左移) >>(右移)
11 1011
14 1110
& 1010 一0则0
| 1111 一1则1
^ 0101 相同为0不同为1
11 << 2 ==> 44 右侧补0
14 >> 2 ==> 3 挤掉低位的数字
使用 : 1. 在做底层硬件的寄存器操作
2. 在做标志位过滤时
2,poll方法实现IO多路复用
- 创建poll对象
p = select.poll() - 注册关注的IO
p.register(s,POLLIN | POLLHUP)
p.unregister(s) 取消IO关注
事件类别POLLIN POLLOUT POLLERR POLLHUP POLLPRI rlist wlist xlist 断开 紧急处理
- 监控IO
events = p.poll()
功能:监控关注的IO事件
返回值:返回发生的IO事件
events 是一个列表[(fileno,event),(),(),...,]
每个就绪IO对应列表中的一个元组,(描述符,就绪事件) - IO地图:{s.fileno():s}
处理IO事件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())
作业:
- 熟练select server 代码
- 了解 IO多路复用原理机制
- 写一个select服务端,同时关注客户端连接,客户端发送和终端输入,将客户端发送和终端输入的内容全部写入到一个文件中。
from socket import * from select import select import sys s = socket() s.bind(("127.0.0.1",9999)) s.listen(5) rlist = [s,sys.stdin] wlist = [] xlist = [] f = open('file.txt','w') while True: rs,ws,xs = select(rlist,wlist,xlist,3) print("***********") for r in rs: if r is s: c,addr = r.accept() rlist.append(c) elif r is sys.stdin: data = r.readline() f.write(data) f.flush() else: data = r.recv(1024) if not data: rlist.remove(r) r.close() else: f.write(data.decode()) f.flush() f.close() s.close()
from socket import * #创建套接字 sockfd = socket() #发起连接 sockfd.connect(('127.0.0.1',8888)) while True: #消息收发 msg = input("Msg>>") if not msg: break sockfd.sendall(msg.encode()) data = sockfd.recv(1024) print(data.decode()) sockfd.close()