学习笔记☞python3☞网络编程(三)☞【tcp之HTTP服务器,IO分类及多路复用】

1、tcp应用之HTTP服务器

    1、接收HTTP请求
    2、给出一定的响应
# 静态网页处理器
# 采用循环的模式,无法满足客户端长连接
import socket


# 处理客户端请求
def handleClient(connfd):
    request = connfd.recv(2048)
    requestHeadlers = request.splitlines()  # 按行分割
    for line in requestHeadlers:
        print(line)
    try:
        f = open('知乎.html', 'r')
    except IOError:
        response = 'HTTP/1.1 404 not found'
        response += '\r\n'
        response += '====sorry,file not find===='
    else:
        response = 'HTTP/1.1 200 OK'
        response += '\r\n'
        for i in f:
            response += i
    finally:
        connfd.send(response.encode())
        connfd.close()


# 流程控制
def main():
    sockfd = socket.socket()
    sockfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sockfd.bind(('0.0.0.0', 8000))
    sockfd.listen(10)
    while True:
        connfd, addr = sockfd.accept()
        handleClient(connfd)


if __name__ == '__main__':
    main()

2、IO input output

    1、IO操作:

        在内存中存在数据交换的操作
        两种操作:
            内存和磁盘交换: 文件读写 打印
            内存和网路的交换:recv send

    2、IO密集型程序

        程序中执行大量的IO操作,而较少需要CPU运算
        消耗CPU资源少,运行周期往往较长

    3、CPU密集型程序

        程序执行中需要大量的CPU运算,IO操作较少
        占用CPU多

3、IO的分类

    1、阻塞IO (默认形态)

        是效率最低的一种IO
        阻塞:因为某种条件达成再继续运行    如:accept recv input
              处理IO时间的时候耗时较长也会产生阻塞   如:文件的读写过程,网络数据的传输过程

    2、非阻塞IO

        通过修改IO对象使其变为非阻塞状态(改变第一种阻塞状态)
        通用循环不断判断阻塞条件,需要消耗更多CPU但是一定程度上提高了IO效率

        1、s.setblocking()
            功能:
                设置套接字的阻塞状态
            参数:
                bool类型,默认为True,设置为false则为非阻塞
import socket as so
import time

s = so.socket(so.AF_INET, so.SOCK_STREAM, 0)
s.setsockopt(so.SOL_SOCKET, so.SO_REUSEADDR, 1)
print(s.getsockopt(so.SOL_SOCKET, so.SO_REUSEADDR))
s.bind(('127.0.0.1', 8888))
s.listen(5)
# 设置s是非阻塞状态
s.setblocking(False)
while True:
    print('等待连接...')
    try:
        connfd, addr = s.accept()
    except BlockingIOError:
        time.sleep(2)
        print(time.ctime())
        continue
    print('连接地址:', addr)
    #设置非阻塞状态
    connfd.setblocking(False)
    while True:
        data = connfd.recv(1024)
        if not data:
            break
        print(data.decode('utf-8'))
        connfd.send('来,确认下眼神!'.encode())
    connfd.close()
soc.close()
        2、超时等待(检测)
            s.settimeout()
                功能:
                    设置套接字的超时监测
                参数:
                    超时时间,单位秒
            所谓超时时间监测既对原本阻塞的函数进行设置,时期不再始终阻塞,而是阻塞等待一定时间后自动返回。
            在规定时间中如果正常结束阻塞则继续执行否则产生timeout异常
import socket as so
import time
import traceback

s = so.socket(so.AF_INET, so.SOCK_STREAM, 0)
s.setsockopt(so.SOL_SOCKET, so.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 8888))
s.listen(5)
# 设置s的超时检测单位秒
s.settimeout(5)
while True:
    print('等待连接...')
    try:
        connfd, addr = s.accept()
    except Exception:
        traceback.print_exc()
        continue
    print('连接地址:', addr)
    #设置超时
    connfd.settimeout(5)
    while True:
        data = connfd.recv(1024)
        if not data:
            break
        print(data.decode('utf-8'))
        connfd.send('来,确认下眼神!'.encode())
    connfd.close()
soc.close()

        ^_^ traceback   更专业的打印异常信息

            import traceback
            # 打印原始错误信息
            traceback.print_exc()

    3、IO多路复用

        定义
            同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件,形成并发的效果
            并发:多个事件同时发生同时处理
            IO事件:
            准备就绪:临界状态
        select模块实现多路复用
            select方法 ---->支持win Linux Unix
            pool方法 ---->支持 Linux Unix
            epool方法 ---->支持 Linux Unix

            1、select:
                r,w,x=select(rlist,wlist,xlist,[timeout])
                    功能:
                        监控IO事件,阻塞等待IO事件发生
                    参数:
                        rlist:
                            列表类型,存放我们要监控等待处理的IO
                        wlist:
                            列表类型,存放我们希望主动处理的IO
                        xlist:
                            列表类型,如果发生异常需要我们处理的IO
                        timeout:
                            数字,超时检测,默认一直阻塞
                    返回值(当任意一个列表中一个IO事件准备就绪就返回):
                        r:
                            列表形式  rlist当中准备就绪的IO
                        w:
                            列表形式  wlist当中准备就绪的IO
                        x:
                            列表形式  xlist当中准备就绪的IO
import sys
import socket
import select

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# s.bind(('172.60.11.118', 8888))
s.bind(('192.168.43.122',8888))
s.listen(10)


# 将关注的IO放入到rlist中
rlist = [s]
wlist = []
xlist = [s]

while True:
    rs, ws, xs = select.select(rlist, wlist, xlist)
    for r in rs:
        # 套接字准备就绪
        if r is s:
            connfd, addr = r.accept()
            print('Connect from', addr)
            # 将新的套接字加入到关注列表
            rlist.append(connfd)
        else:
            try:
                data = r.recv(1024)
                if not data:
                    rlist.remove(r)
                    r.close()
                print('Received from',r.getpeername(),':', data.decode())
                wlist.append(r)
            except Exception:
                pass
    for w in ws:

        w.send('>>> '.encode())
        wlist.remove(w)
    for x in xs:
        if x is s:
            s.close()
            sys.exit()

           IO多路复用注意点:
                1、在处理IO过程中不应该发生死循环(某个IO单独占有服务器)
                2、IO多路复用是单进程程序,是一个并发程序
                3、IO多路复用有较高的IO执行效率

        2、pool
            1、创建pool对象
                p = select.poll()
            2、加入关注的IO
                p = register(s)
            3、使用poll函数监控
                events = p.poll() # 阻塞函数,和select.poll()不同
                    功能:
                        阻塞等待register的事件只要有任意准备就绪及返回
                    返回值:
                        events为列表格式 [(fileno,event),(),()]
                poll IO事件
                    POLLIN      rlist
                    POLLOUT     wlist
                    POLLUP      断开连接
                    POLLERR     xlist
                    POLLPRI     紧急处理
                    POLLVAL     无效数据
                s & POLLIN
            4、处理发生的IO事件
            示例:
import socket
import select

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# s.bind(('172.0.0.1', 8888))
s.bind(('0.0.0.0', 8888))
s.listen(10)

# 创建IO事件地图
fdmap = {s.fileno(): s}
# 创建poll对象
p = select.poll()
# 将套接字加入到关注
p.register(s, (select.POLLIN | select.POLLERR))
while True:
    # 进行监控
    events = p.poll()
    print(events)
    for fd, event in events:
        if fd == s.fileno():
            c, addr = fdmap[fd].accept()
            print('connect from', addr)
            p.register(c, select.POLLIN)
            fdmap[c.fileno()] = c
        elif event & select.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())
        3、epoll
            1、效率上比poll和select稍微高
            2、只能用于Linux和Unix
            3、支持边缘触发  select 和 poll 只支持水平触发

    4、事件驱动IO

    5、异步IO


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值