python 网络编程 day03

2018.8.8

目录

tcp应用之 httpserver

IO

位运算


day02回顾

tcp套接字

  1. 网络缓存区
  2. 粘包

udp 套接字

  1. 服务端 socket(AF_INET,SOCK_DGRAM)
    bind()
    recvfrom()   sendto()
  2. 客户端 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

  1. 在内存中存在数据交换的操作可以认为是IO操作(输入输出)
  2. 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

  1. 阻塞IO:程序运行中遇到IO条件没有达成或者传输过从较慢的情况会出现阻塞状态

    阻塞IO是 IO最简单的逻辑情形,也是默认状态

    阻塞IO是效率很低的IO形态

    阻塞情况:因为某种IO条件没有达到形成阻塞
    e.g.:accept  recv  recvfrom  input
      * 处理IO事件的时候耗时比较长形成阻塞
      e.g.  文件的读写过程,网络数据发送过程
  2. 非阻塞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()
  3. IO多路复用
    定义:通过一个监测,可以同时监控多个IO事件的行为,当哪个IO事件可以执行,即让这个IO事件发生。以此形成,多个IO事件都可以操作,不必诸个等待执行的效果。

    IO事件就绪:IO事件即将发生的一个临界的状态

    1,在程序中存在的IO事件中选择要监测的
    2,创建监测,将监测的IO事件注册
    3,等待监测的IO事件发生,判断是什么IO事件
    4,处理响应的IO

    import select
    (创建IO多路复用的方法)

    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

    注意事项:
      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多路复用

  1. 创建poll对象
    p = select.poll()
  2. 注册关注的IO
    p.register(s,POLLIN | POLLHUP)
    p.unregister(s) 取消IO关注

    事件类别
    POLLIN  POLLOUT  POLLERR  POLLHUP   POLLPRI
    rlist   wlist    xlist    断开      紧急处理
  3. 监控IO
    events = p.poll()
    功能:监控关注的IO事件
    返回值:返回发生的IO事件
    events 是一个列表[(fileno,event),(),(),...,]
    每个就绪IO对应列表中的一个元组,(描述符,就绪事件)
  4. 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())

     

作业:

  1. 熟练select server 代码
  2. 了解 IO多路复用原理机制
  3. 写一个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()

猜你喜欢

转载自blog.csdn.net/qq_42584444/article/details/81501982
今日推荐