• 2019.9.9学习内容及随堂笔记


    小结

    随口补充:http协议:应用层,底层基于socket,无状态无连接,版本有1.0,1.1(目前主流) 最新2.0
    上节课回顾:

    1. 加入通信循环,使客户端和服务端可以相互发送数据

    2. 加入连接循环, 可以接收多个客户端的连接请求

    3. 执行ssh命令的小案例subprocess
      -psutil模块,统计机器的内存占用率,硬盘占用率。。。。(有兴趣可以看)

    4. 粘包问题

      tcp协议是流式协议

      tcp协议会把数据量较小,时间间隔较短的数据,一次性发送

      5. 解决粘包问题(代码略)
      

    今日内容:

    基于socket的udp

    ​ 重点:sendto recvfrom

    udp协议的特点:

    ​ 1.可以发空(数据报协议,自带头)

    ​ 2.不需要建连接

    ​ 3.不会粘包

    ​ 4.不可靠(客户端、服务端谁断开都不受影响)
    socketserver的使用(并发)
    -tcp的服务端
    ​ -server=ThreadingTCPServer 对象
    ​ -server.serve_forever()

    ​ -写一个类,类里面重写handler方法,方法内收发数据(实现并发起来了)
    -udp的服务端
    ​ -server=ThreadingUDPServer 对象
    ​ -server.serve_forever
    ​ -写一个类,类里重写handler方法,方法内收发数据(实现并发)
    -self.request(tcp/udp是不一样的)
    -self.client_address 客户端地址

    socketserver源码分析

    -ThreadingTCPServer: init:创建socket,bind,listen

    -server.serve_forever(): 创建线程,建立连接,和处理通信

    上节课回顾

    json模块

    import json
    # 把字典格式转成json格式字符串
    #dic = {"name": "lqz", "xx": False, "yy": None}
    #print(type(dic)) #<class 'dict'>
    #dic = json.dumps(dic)
    # print(dic) #{"name": "lqz", "xx": false, "yy": null} /json字符串
    # print(type(dic)) #<class 'str'>
    
    # 以下不是json字符串,注意,就是普通的字符串类型
    # dic2 = str(dic)
    # print(dic2) # {'name': 'lqz', 'xx': False, 'yy': None}
    # print(type(dic2)) #<class 'str'>
    
    
    '''
    ####### 用法:
    json.dumps()  # 把数据类型转成json字符串
    json.dump()   # 把数据类型转成json字符串,并存到文件中
    
    json.loads()  # 把json格式字符串转成数据类型 (字典、列表等)
    json.load()   # 从文件中把json格式字符串转成数据类型
    '''
    
    
    # 不是json格式字符串转换,直接报错
    # ss = 'falsedddddd'
    # # # json.loads()  # 从python3.6以后,支持bytes格式直接转
    # s = json.loads(ss) # 报错,ss不是json格式字符串
    # print(type(s))
    # print(s)
    
    
    # sss = {"name": "lqz", "xx": False, "yy": None}
    # s = json.dumps(sss) #{"name": "lqz", "xx": false, "yy": null}
    # s = json.loads(sss)
    # print(s)
    
    '''
    import os
    size = os.path.getsize('E:老男孩学习内容与笔记day32作业client.py')
     #获取一个文件里面所有的字节数量
    print(size) # 4140
    '''
    
    服务端
    import socket
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    while True: # 连接循环
        #一旦有个客户连接成功,会返回两个值,如果没有,会一直卡在这
        print('等待客户端连接: ')
        conn, addr = server.accept()
        print('有个客户端已连接', addr)
    
        while True: # 通信连接
            # 卡在这里,等待客户端来发
            print('等待接收数据:')
            data = conn.recv(1024)
            print(data)
            conn.send(data.upper())
        conn.close()
    server.close()
    
    客户端
    
    import socket
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))
    while True:
        msg = input('请输入你要发送的数据:')
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data)
    client.close()
    
    服务端——粘包
    import socket
    import os
    import json
    import struct
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    
    def download_interface():
        while True:  # 连接循环
            # 一旦有客户连接成功,会返回两个值(conn, addr),如果没有,会卡在这里
            conn, addr = server.accept()
            while True: #通信循环
    
                #1.加个文件头
                #{'size': os.path.getsize(), name:'xxx.py'}
    
                #2.计算一下头的长度
                dic = {'size': os.path.getsize('json模块.py'), 'name': 'xxx.py'}
    
                #3.转成bytes格式, 此处以json模块为例
                dic_s = json.dumps(dic)
                dic_b = bytes(dic_s,encoding='utf-8')
    
                #4.获得长度
                dic_b_len = len(dic_b)
    
                # 通过struct模块 将数据长度打包成固定长度字节
                obj = struct.pack('i', dic_b_len)
    
                # 5.发送四个字节
                conn.send(obj)
    
                # 6.发送头部内容
                conn.send(dic_b)
    
                # 7.直接以+形式打开文件,就不需要转换了  rb模式
                with open('json模块.py', 'rb')as f:
                    #conn.send(f.read()) 不能这样运行,如果文件过大,内存无法负载
                # 注:文件是可以迭代的
                    for line in f:
                        conn.send(line)
            conn.close()
        server.close()
    
    def upload_interface():
        while True:  # 连接循环
            # 一旦有客户连接成功,会返回两个值(conn, addr),如果没有,会卡在这里
            conn, addr = server.accept()
            while True: # 通信循环 (下载)
    
    
                #1.先收4个字节
                head=conn.recv(4)
    
                #2.取出头部的长度
                head_len = struct.unpack('i', head)[0]
    
                #3.接收头部
                head = conn.recv(head_len)
    
                #4.通过json反序列化拿到原字典
                dic = json.loads(head)
    
                #5.拿到字典后取出内容真正的长度,以及文件名
                l = dic['size']
                name = dic['name']
    
                # 准备接收
                count = 0
                data = b''
                with open(name, 'wb')as f:
                    while count < l:
                        if l < 1024:
                            data_temp = conn.recv(l)
                        else:
                            if l - count >= 1024:
                                data_temp = conn.recv(1024)
                                print(data_temp)
                            else:
                                data_temp = conn.recv(l - count)
                        data += data_temp
                        count += len(data_temp)
                    f.write(data)
                print('上传完成')
                conn.send('ok'.encode())
    
    客户端——粘包
    
    import socket
    import struct
    import json
    import os
    
    user_info = {'user': None}
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    
    def register():
        while True:
            print('欢迎来到注册功能')
            if not user_info['user']:
                username = input('username').strip()
                pwd = input('pwd').strip()
                re_pwd = input('pwd')
                if re_pwd == pwd:
                    with open('user_info.test', 'w',encoding='utf-8')as f:
                        f.write(f'{username}:{pwd}')
                        print(f'{username}注册成功')
                        break
                else:
                    print('两次密码不一致')
    
    def login():
        while True:
            print('欢迎来到登录功能:')
            username = input('username:').strip()
            pwd = input('pwd:').strip()
            inp_user_info = f'{username}:{pwd}'
            with open('user_info.test', 'r',encoding='utf-8')as f:
                userinfo = f.read()
                if inp_user_info not in userinfo:
                    print('账户或密码错误')
                    break
                else:
                    print(f'{username}登录成功')
                    user_info['user'] = username
    
    
    
    
    
    
    
    
    def download():
        while True: # 通信循环 (下载)
            msg = input('输入任意键下载:')
    
            #1.先收4个字节
            head=client.recv(4)
    
            #2.取出头部的长度
            head_len = struct.unpack('i', head)[0]
    
            #3.接收头部
            head = client.recv(head_len)
    
            #4.通过json反序列化拿到原字典
            dic = json.loads(head)
    
            #5.拿到字典后取出内容真正的长度,以及文件名
            l = dic['size']
            name = dic['name']
    
            # 准备接收
            count = 0
            data = b''
            with open(name, 'wb')as f:
                while count < l:
                    if l < 1024:
                        data_temp = client.recv(l)
                    else:
                        if l - count >= 1024:
                            data_temp = client.recv(1024)
                            print(data_temp)
                        else:
                            data_temp = client.recv(l - count)
                    data += data_temp
                    count += len(data_temp)
                f.write(data)
            print('接收完成')
        client.close()
    
    
    
    def upload():
        while True: #通信循环 (上传)
            msg = input('输入任意键上传:')
    
            #1.加个文件头
            #{'size': os.path.getsize(), name:'xxx.py'}
    
            #2.计算一下头的长度
            dic = {'size': os.path.getsize('json模块.py'), 'name': 'xxx.py'}
    
            #3.转成bytes格式, 此处以json模块为例
            dic_s = json.dumps(dic)
            dic_b = bytes(dic_s,encoding='utf-8')
    
            #4.获得长度
            dic_b_len = len(dic_b)
    
            # 通过struct模块 将数据长度打包成固定长度字节
            obj = struct.pack('i', dic_b_len)
    
            # 5.发送四个字节
            client.send(obj)
    
            # 6.发送头部内容
            client.send(dic_b)
    
            # 7.直接以+形式打开文件,就不需要转换了  rb模式
            with open('json模块.py', 'rb')as f:
                #conn.send(f.read()) 不能这样运行,如果文件过大,内存无法负载
            # 注:文件是可以迭代的
                for line in f:
                    client.send(line)
            data = client.recv(1024)
            print(data.decode())
        client.close()
    
    
    func_dic = {'1': register,
                '2': login,
                '3': download,
                '4': upload
                }
    
    def run():
        while True:
            print('''
            1. 注册
            2. 登录
            3. 下载
            4. 上传
            q. 退出
            ''')
            choice = input('请选择需要的功能:')
            if choice == 'q':
                break
    
            if choice not in func_dic:
                print('选择不存在')
                continue
            func_dic.get(choice)()
    

    socketserver模块

    socketserver_服务端
    # 使用socketserver 写服务端
    # 导入模块
    
    import socketserver
    
    # 自己定义一个类,必须继承BaseRequestHandler
    class MyTcp(socketserver.BaseRequestHandler):
        # 必须重写 handler 方法
        def handler(self):
            try:
                while True: # 通信循环
                    print(self)
                    # 给客户端回消息
                    # conn 对象就是 request
                    # 接收数据
                    print(self.client_address)
                    data = self.request.recv(1024)
                    print(data)
                    if len(data) == 0:
                        return
                    # 发送数据
                    self.request.send(data.upper())
            except Exception:
                pass
    if __name__ == '__main__':
        # 实例化得到一个tcp链接的对象,Threading意思是说,只要来了请求,它自动的开线程来处理连接跟交互数据
        # 第一个参数是绑定的地址,第二个参数是传一个类
        server = socketserver.ThreadingTCPServer(('127.0.0.1',8089), MyTcp)
        #一直在监听
        # 这么理解:只要来了一个请求,就起一个线程(造一个人,做交互)
        server.serve_forever()
    
    client_客户端
    import socket
    import time
    
    soc = socket.socket()
    
    soc.connect(('127.0.0.1', 8089))
    
    while True:
        soc.send('xxxxxx'.encode('utf-8'))
        data = soc.recv(1024)
        print(data)
    
    

    socketserver模块的udp

    socketserver_服务端
    # 使用socketserver写服务端
    # 导入模块
    import socketserver
    
    #自己定义一个类,必须继承BaseRequestHandler
    class MyTcp(socketserver.BaseRequestHandler):
        def handler(self):
            print(self)
            #数据
            print(self.request[0])
            print(self.request[1])
            print(type(self.request[1]))
            self.request[1].sendto('xxxx'.encode('utf-8'), self.client_address)
            # try:
            #     while True: 通信循环
            #         data = self.request.recvfrom(1024)
            #         print(data)
            # except Exception:
            #     pass
    
    if __name__ == '__main__':
        # 实例化得到一个tcp连接的对象,Threading意思是说,只要来了请求,它自动的开线程来处理连接跟交互数据
        #第一个参数是绑定的地址,第二个参数传一个类
        server = socketserver.ThreadingUDPServer(('127.0.0.1',8081), MyTcp)
        #一直在监听
        server.serve_forever()
    
    client_客户端
    import socket
    #udp
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    client.sendto('lqz'.encode('utf-8'),('127.0.0.1',8080))
    # client.sendto('hello'.encode('utf-8'),('127.0.0.1',8080))
    # client.sendto(''.encode('utf-8'),('127.0.0.1',8080))
    data = client.recvfrom(1024)
    print(data)
    
    client2_服务端
    import socket
    import time
    soc = socket.socket()
    
    soc.connect(('127.0.0.1',8081))
    while True:
        soc.send('yyy'.encode('utf-8'))
        print(soc.recv(1024))
    

    udp协议

    udp_服务端
    # 基本版本
    # import socket
    # # udp
    # server = socket.socket(type=socket.SOCK_DGRAM)
    #
    # server.bind(('127.0.0.1',8080))
    #
    # # udp不要建立连接,直接发
    # #需不需要监听
    # # 跟tcp的是不一样的
    # ##data = server.recvfrom(1024)
    # data,addr = server.recvfrom(1024)
    # ## data是一个元组,一个参数是数据部分,第二个参数是客户地址
    # print(data)
    # server.sendto(data.upper(),addr)
    
    # 加入通信循环
    import socket
    #udp
    server = socket.socket(type=socket.SOCK_DGRAM)
    
    server.bind(('127.0.0.1',8080))
    
    while True:
        data, addr = server.recvfrom(1024)
        print(data)
        server.sendto(data.upper(), addr)
    
    udp_客户端
    # import socket
    # ## udp
    # client = socket.socket(type=socket.SOCK_DGRAM)
    #
    # # 直接发
    # client.sendto(b'lqz',('127.0.0.1', 8080))
    # data = client.recvfrom(1024)
    # print(data)
    
    #加入通信循环
    import socket
    #udp
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    while True:
        msg = input('>>>:')
        #直接发
        client.sendto(msg.encode('utf-8'),('127.0.0.1', 8080))
        data = client.recvfrom(1024)
        print(data)
    

    udp协议是否丢包

    udp_服务端
    # udp 协议没有粘包问题(udp协议又叫数据报协议),可以发空,tcp协议不行
    import socket
    server = socket.socket(type=socket.SOCK_DGRAM)
    server.bind(('127.0.0.1', 8080))
    while True:
        data, addr = server.recvfrom(1024)
        print(data)
        server.sendto(data.upper(), addr)
    
    udp_客户端
    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    while True:
        msg = input('>>>:')
        client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
        data = client.recvfrom(1024)
        print(data)  #(b'HELLO', ('127.0.0.1', 8080))
        print(data[0]) #b'HELLO' 通过索引来取出数据内容
        # 注:此处客户端接收“client.recvfrom(1024)”会返回两个值,一个是数据,一个是地址,如果只用一个变量接收,那么会的到一个元组,里面包换数据及服务端ip地址
    
        # data, addr = client.recvfrom(1024)
        # print(data) #'HELLO'
        # print(addr) #('127.0.0.1', 8080)
    
    
    udp_客户端丢包
    import socket
    # udp 不会管客户端或者服务端是否收到,它只管发,所以不可靠
    client = socket.socket(type=socket.SOCK_DGRAM)
    
    while True:
        msg = input('>>>:')
        client.sendto(msg.encode('utf-8'),('127.0.0.1', 8080))
    
        data, addr = client.recvfrom(1024)
        print(data)
    
    
    # udp特点:
    #     可以发空(udp协议是数据报协议,自带头,tcp协议是数据流协议)
    #     客户端和服务端可以有一方没在线(因为不需要建立连接)
    
  • 相关阅读:
    UEmacs/PK Keybindings
    ubunut install golang
    vyos ipsec l2tp simple experiment
    Lynx 命令行操作
    Linux开启路由转发功能
    PROTEUS的元器件及模型制作
    5G模拟网自动化测试的需求和挑战
    vyos site-to-site ipsec simple experiment
    kubeadm 搭建 k8s 时用到的常用命令汇总
    html 特殊符号标记
  • 原文地址:https://www.cnblogs.com/chmily/p/11497874.html
Copyright © 2020-2023  润新知