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()
S.splitlines 拆分文件字符串按行分隔
什么是IO?
- 分为IO设备和IO接口两个部分
- 如Linux系统,I/O操作可以有多种方式
- 比如DIO(DirectI/O)
- AIO(AsynchronousI/O异步I/O)
- Memory-MappedI/O(内存映设I/O)等...
- 不同的I/O方式有不同的实现方式和性能,在不同的应用中可以按情况选择不同的I/O方式。
在内存中存在数据交换的操作可以认为是IO操作(输入输出) 例如: 内存与磁盘数据交换:文件读写、数据库更新 内存和终端数据交换:input、print、sys.stdout、sys.stdin、 sys.stder 内存和网络数据交换:网络连接、recv、send、recvfrom
IO秘集程型序: 程序执行中有大量的IO操作,而比较少的CPU运算操作 消耗CPU较少,IO运行时间长 cpu(计算)密集形程序: 程序存在大量的CPU运算,IO操作相对较少 CPU消耗大
IO分类: 1.阻塞IO: 程序运行中遇到IO条件没有达成 或传输情况较慢的情况下会出现阻塞状态 阻塞IO是IO最简单的逻辑情形,也是默认状态 阻塞IO是效率很低的IO状态 阻塞情况: 1.因为IO条件没有达成 IO阻塞函数(input、print、recv、recvfrom) 2.处理IO耗时较长形参阻塞 文件读写、网络数据发送过程
2.非阻塞IO: 在程序运行中遇到IO操作不让其产生阻塞 实现手段: 改变IO事件的属性,使其变为非阻塞 通常会和循环一起使用 进行条件的循环监控
3.IO多路复用 定义: 通过一个监测,可以同时监控多个IO事件的行为, 当那个IO可以执行,让这个IO事件发生 同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件 此时形成多个IO时间都可以操作的现象,不必逐个等待执行 IO准备就绪: IO事件即将发生时的临界状态是不可逆转的 在程序中存在的IO事件中选择要监测的事件 创建监测,将监测的IO事件注册 等待监测的IO事件发生,判断是什么事件 处理相应的IO
事件驱动IO 异步IO ... s.setblocking(False) 功能: 将套接字设置为非阻塞状态 参数: 默认为阻塞, 设置为False为非阻塞状态
超时检测: 将原本阻塞的IO设置一个最长阻塞等待时间, 在规定时间内如果达到条件则正常执行, 如果时间到仍未达到条件则结束阻塞。 s.settimeout(sec) 功能: 设置套接字的超时时间 参数: 时间(秒)
select模块 from select import 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 列表: 准备就绪的IO ws 列表: 准备就绪的IO xs 列表 准备就绪的IO
* 在处理IO时不要形成死循环,会让一个客户端单独占有服务端
* IO多路复兴形成一种可以同时处理多个IO的效果,效率较高
位运算: 按照二进制位进行位运算操作 & 按为与 | 按位或 ^ 按位异或
<< 左异 >>右移
11 1011 14 1110
& 1010 有0得0 | 1111 有1得1 ^ 0101 相同为0不同为1 11 << 2 == 44 右侧补零(乘2乘2次) 14 >> 2 == 3 挤掉右侧的数字(地板除2除2次) 使用: 1.在低层硬件时操作寄存器 2.做标志位的过滤
poll方法实现IO多路复用: 1.创建poll对象: p = select.poll 2.注册关注的IO: p.register(s, POLLIN | PLLERR) 不关注: p.unregister(s) 事件类别: POLLIN POLLOUT POLLERR POLLHUP POLLPRI rlist wlist xlist 断开 紧急处理 3.监控IO: events = p.poll() 功能:监控关注的IO事件 返回值: 返回发生IO事件 events是一个列表[(fileno, evnet), (), ()....] 每个就绪IO对应一个元组(描述符,就绪事件) IO地图:{s.fileno():s} 4.处理IO事件
select 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)
# 循环遍历rs准备就绪列表
for r in rs:
# 如果有新的客户端链接请求
if r is s:
# 链接客户端并返回客户端套接字
connfd, addr = r.accept() # r==s 原套接字
print("Connect from", addr)
# 将绑定客户端套接字加入监听列表
rlist.append(connfd)
# 表示客户端连接套接字准备就绪
else:
# 如果是客户端套接字发送数据 则接收
data = r.recv(1024)
if not data: # 如果客户端断开链接
# 从关注列表移除connfd
rlist.remove(r)
r.close() # 关闭套接字
else:
print("Receive:", data.decode())
# 讲客户端套接字放入wlist
wlist.append(r)
# wlist列表会直接返回
# 循环遍历ws准备就绪列表
for w in ws:
# 消息回复
w.send("这是一条回复消息".encode())
# 删除并取消监听已处理消息
wlist.remove(w)
# xs列表:待处理异常
for x in xs:
if x is s:
s.close()
sys.stdin
客户端:
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()
应用 : select服务端,同时关注客户端连接 客户端发送和终端输入。将客户端发送和终端输入的内容全都写入到一个文件中
# myserver.py
# 应用 :
# select服务端,同时关注客户端连接
# 客户端发送和终端输入。将客户端发送和终端输入的内容全都写入到一个文件中
from socket import *
from select import *
from sys import stdin
sock = socket()
sock.getsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sock.bind(("127.0.0.1", 6666))
sock.listen(5)
rlist = [sock, stdin]
wlist = []
xlist = []
file = open("selectdemo.txt", "w+b")
while True:
rs, ws, xs, = select(rlist, wlist, xlist)
for r in rs:
if r == sock:
connfd, addr = r.accept()
print("已连接......")
rlist.append(connfd)
elif r == stdin:
data = stdin.readline()
file.write(data.encode())
file.flush()
else:
data = r.recv(4096)
if not data:
rlist.remove(r)
r.close()
else:
file.write(data + "\n".encode())
file.flush()
print("已经接收内容并写如select>>>.txt文件内\n 内容:", data.decode())
r.send("接收成功!".encode())
file.close()
from socket import *
# 创建套接字
sockfd = socket()
# 发起连接
sockfd.connect(('127.0.0.1', 6666))
while True:
# 消息收发
msg = input("Msg>>")
if not msg:
break
sockfd.sendall(msg.encode())
data = sockfd.recv(1024)
print(data.decode())
sockfd.close()