zoukankan      html  css  js  c++  java
  • 单例模式,网络编程之tcp协议以及粘包问题

    网络编程tcp协议与socket以及单例的补充

    一、单例补充

    • 实现单列的几种方式
    #方式一:classmethod
    
    # class Singleton:
    #
    #     __instance = None
    #
    #     @classmethod
    #     def singleton(cls):
    #
    #         if not cls.__instance:
    #             cls.__instance = cls()
    #
    #         return cls.__instance
    #
    # obj1 = Singleton.singleton()
    # obj2 = Singleton.singleton()
    # print(obj1)
    # print(obj2)
    # <__main__.Singleton object at 0x000002127F230D08>
    # <__main__.Singleton object at 0x000002127F230D08>
    
    
    #方式二: __new__
    # class Singleton:
    #
    #     __instance = None
    #
    #     def __new__(cls, *args, **kwargs):
    #
    #         if not cls.__instance:
    #             cls.__instance = object.__new__(cls)
    #
    #         return cls.__instance
    #
    # obj1 = Singleton()
    # obj2 = Singleton()
    # print(obj1)
    # print(obj2)
    # <__main__.Singleton object at 0x00000257AAE10A88>
    # <__main__.Singleton object at 0x00000257AAE10A88>
    
    
    #方式三:装饰器
    # def singleton(cls):  #cls---> Father
    #
    #     __instance = {}
    #
    #     def inner(*args, **kwargs):
    #         if cls not in __instance:
    #             obj = cls(*args, **kwargs)
    #             __instance[cls] = obj
    #
    #
    #         return __instance[cls]
    #     return inner
    #
    # @singleton
    # class Father:
    #     pass
    #
    # print(Father())
    # print(Father())
    # <__main__.Father object at 0x000001F17EB21548>
    # <__main__.Father object at 0x000001F17EB21548>
    
    
    
    #方式四
    先定义一个Singletoncls的py文件内容如下:
    class Singletoncls:
        pass
    
    obj = Singletoncls()
    # from Singletoncls import obj
    # print(obj)
    # from Singletoncls import obj
    # print(obj)
    # from Singletoncls import obj
    # print(obj)
    # <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
    # <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
    # <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
    
    # 方式五:元类
    
    
    

    二、tcp协议以及粘包问题

    • 理论知识
    传输层:
        -tcp协议
        -udp协议
        端口(port):标识一台计算机上的某一个软件。
            -0-1024:禁止使用,因为是操作系统在用
            -8000---->以后接着用
    
        - 以下的一些软件的固定端口不要碰:
            django:8000
            mysql:3306
            redis:6379
            flask:5000
            tomcat:8080
            mongodb:27017
            ...
        要想要传输数据,必须建立双向通道
    
    
    1、tcp协议:三次握手,四次挥手
        -tcp协议建立双向通道
    
        - 三次握手,键连接:
            1:客户端向服务端发送建立连接的请求
            2:服务端返回收到的请求信息给客户端,并且发送往客户端建立的请求
            3:客户端接收到服务端的请求,返回请求成功给服务端,完成双向连接
    
    
        - 反馈机制:
            客户端往服务端发送请求,服务端必须返回响应
            告诉客户端收到请求了,并且将服务端的数据一并返回给客户端
            c--->s:一次请求,必须有一次响应
    
            - 缺点:
                - 洪水攻击:
                    指的是通过伪造大量的请求,往对方服务器发送请求
                    导致对方服务器跟不上,以至于瘫痪。
                    Linux系统有个参数可以限制
    
                - 半连接池listen:限制用户在同一时间段内的访问数量
    
        - 四次挥手,断开连接
            1:客户端向服务端发送断开连接的请求
            2:服务端返回收到请求的信息给客户端
            3:服务端确认所有的数据发送完成以后,再发送同意断开连接的请求给客户端
            4:客户端返回收到断开连接的请求,给服务端
    
    2、socket套接字通信:
        - 什么是socket?
            socket是一个模块,又称套接字,用来封装 互联网协议(应用层以下的层)
    
        - 为什么要有socket?
            socket可以实现 互联网协议应用层已下的层的工作
            - 提高开发效率
    
        - 怎么使用socket?
            import socket
            写socket套接字:
                client
                sever
    
    3、粘包问题
        -1)问题:无法确认对方发送过来的数据的大小
    
        -2)问题:在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送
    
        解决:确认对方数据的大小
    
    4、解决粘包问题(struct模块)
        -无论那一段发送数据
            -客户端
                -1)先制作报头,并发送(struct)
                -2)发送真实数据
    
            服务端
                -1)接收报头,并解包获取 真实的数据长度
                -2)根据真实数据长度 接收真实数据
                recv(真实数据长度)
    
    
    

    三、socket套接字

    • socket套接字初级版本
    • 演示
    -sever:
        
    '''
    先启动套接服务端
    '''
    
    import socket
    
    #买手机
    sever = socket.socket()
    
    #绑定手机卡
    sever.bind(
        ('127.0.0.1', 9876)
    )
    
    #半连接池
    sever.listen(5)  #最多同时五个人坐椅子,实际上==6
    
    print(
        '服务端正在运行...'
    )
    
    #等待电话接入
    #coon:指的是服务端往客户端的管道
    coon, addr = sever.accept()
    
    #接听对方讲话的内容
    #data客户端发送过来的消息
    data = coon.recv(1024) #一次性可接受1024bytes的数据
    print(data)
    
    >>>服务端正在运行...
    >>>b'hello'
    
    #挂电话
    coon.close()
    
    
    -client:
    '''
    先启动服务端后再启动客户端
    '''
    
    import socket
    
    #买手机
    client = socket.socket()
    
    #拨打电话
    client.connect(
        ('127.0.0.1', 9876)
    )
    
    print('客户端正在运行...')
    
    #必须发送bytes类型的数据
    #开始讲话
    client.send(b'hello')
    # 或client.send('hello'.encode('utf_8'))
    >>>客户端正在运行...
    
    • socket套接字升级版本
    - sever
    
    '''
    注意:
        客户端法送一次,我服务端就得先接受一次,之后才可以再向客户端发送消息
    '''
    
    import socket
    
    #买手机
    sever = socket.socket()
    
    #绑定手机卡
    #里面绑定的是一个元祖
    sever.bind(
        ('127.0.0.1', 9876)
    )
    
    
    #半连接池
    sever.listen(5)
    
    print('服务端正在等待服务...')
    
    #等待电话接入
    #coon:指的是服务端往客户端的管道
    
    coon, addr = sever.accept()
    
    #接听对方讲话的内容
    data = coon.recv(1024)
    print(data)
    
    #服务端往客户端发送消息
    coon.send(b'hi i am sever')
    
    #挂电话
    coon.close()
    
    >>>服务端正在等待服务...
    >>>b'hello i am client...'
    
    
    - client
    '''
    启动服务端后再启动客户端
    '''
    import socket
    
    #买手机
    client = socket.socket()
    
    #拨号
    client.connect(
        ('127.0.0.1', 9876)
    )
    
    print('客户端正在发送请求...')
    
    client.send(b'hello i am client...')
    
    #接收服务端的请求
    data = client.recv(1024)
    
    print(data)
    
    client.close()
    >>>客户端正在发送请求...
    >>>b'hi i am sever'
    
    • socket套接字高级版本
    -sever
    
    import socket
    
    #买手机
    sever = socket.socket()
    
    
    #绑定手机卡
    sever.bind(
        ('127.0.0.1', 9876)
    )
    
    #半连接池
    sever.listen(5)
    
    print('服务端正在运行...')
    
    #等待电话接入
    coon, addr = sever.accept()
    
    while True:
        #接收对方讲话内容
        #data客户端发送过来的消息
        data = coon.recv(1024)
        if len(data) == 0:
            break
        if data.decode('utf-8') == 'q':
            break
        print(data.decode('utf-8'))
    
        send_data = input('服务端...')
    
        coon.send(send_data.encode('utf-8'))
    
    #挂电话
    coon.close()
    服务端正在运行...
    服务端...你好啊亚峰
    
    -client
    
    import socket
    
    #买手机
    client = socket.socket()
    
    #拨打电话
    client.connect(
        ('127.0.0.1', 9876)
    )
    
    print('客户端正在发送请求...')
    
    while True:
        send_data = input('客户端>>>:').strip()
        client.send(send_data.encode('utf-8'))
        data = client.recv(1024)
        if data.decode('utf-8') == 'q':
            break
        if len(data) == 0:
            break
    
        print(data.decode('utf-8'))
    
    client.close()
    >>>客户端正在发送请求...
    >>>客户端>>>:你好啊热巴
    >>>好啊亚峰
    
    • socket套接字终级版本
    - sever
    
    
    import socket
    
    #买手机
    sever = socket.socket()
    
    #绑定手机卡
    sever.bind(
        ('127.0.0.1', 9876)
    )
    
    #半连接池
    sever.listen(5)
    
    print('服务端正在服务...')
    
    #循环实现可接受多个用户访问
    while True:
    
        coon, addr = sever.accept()
        print(addr)
    
        #循环实现通信
        while True:
            try:
                #监听代码是否有异常出现
                #接听对方讲话的内容
                #data客户端发送过来的消息
                data = coon.recv(1024)
    
                if len(data) == 0:
                    break
    
                if data.decode('utf-8') =='q':
                    break
    
                print(data.decode('utf-8'))
    
                send_data = input('服务端>>>...')
    
                #服务端向客户端发送消息
                coon.send(send_data.encode('utf-8'))
    
            except Exception as e:
                print(e)
                break
    
        #挂电话
        coon.close()
    
    >>>
    服务端正在服务...
    ('127.0.0.1', 52467)
    我想找迪丽热巴
    服务端>>>...你好啊亚峰,我是热巴,有什么能帮你吗
    热巴youare beautiful
    服务端>>>...谢谢亚峰
    
    
    -client
    
    import socket
    
    #买手机
    
    client = socket.socket()
    
    #拨打号码
    client.connect(
        ('127.0.0.1', 9876)
    )
    
    print('客户端正在发送请求...')
    
    while True:
        send_data = input('客户端>>>:')
        client.send(send_data.encode('utf-8'))
        data = client.recv(1024)
    
        if data.decode('utf-8') == 'q':
            break
    
        if len(data) == 0:
            break
    
        print(data.decode('utf-8'))
    >>>
    client.close()
    客户端正在发送请求...
    客户端>>>:我想找热巴
    你好啊亚峰,我是热巴,有什么能帮你吗
    客户端>>>:热巴you are beautiful
    谢谢亚峰
    客户端>>>:
    

    四、粘包问题

    • 粘包问题的出现以及几种情况
    • 第一个问题
    -sever
    #问题一不知道数据的具体长度
    # import socket
    #
    # import subprocess
    #
    # #买手机
    # sever = socket.socket()
    #
    # #绑定电话卡
    # sever.bind(
    #     ('127.0.0.1', 9867)
    # )
    #
    # #半整数池
    # sever.listen(5)
    #
    # while True:
    #     coon, addr = sever.accept()
    #     print(addr)
    #
    #     while True:
    #         try:
    #             #recv从内存中获取数据
    #             cmd = coon.recv(1024)
    #
    #             if len(cmd) == 0:
    #                 continue
    #             cmd = cmd.decode('utf-8')
    #             if cmd == 'q':
    #                 break
    #
    #             #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
    #             obj = subprocess.Popen(
    #                 cmd, shell=True, stdout=subprocess.PIPE,
    #                 stderr=subprocess.PIPE
    #             )
    #             #结果交给result变量名
    #             result = obj.stdout.read() + obj.stderr.read()
    #             print(len(result))
    #
    #             #windows默认是gbk
    #             print(result.decode('gbk'))
    #
    #             #将结果返回给客户端
    #             coon.send(result)
    #         except Exception as e:
    #             print(e)
    #             break
    #
    #     coon.close()
    
    
    -client
    # import socket
    #
    # client = socket.socket()
    #
    # client.connect(
    #     ('127.0.0.1', 9867)
    # )
    #
    # while True:
    #
    #     cmd = input('客户端输入的内容:')
    #
    #     client.send(cmd.encode('utf-8'))
    #
    #     data = client.recv(19190)
    #
    #     print(len(data))
    #
    #     print(data.decode('gbk'))
    
    • 第二种问题
    -sever
    
    #问题二:当发送多次传入的数据长度却不是很长的时候,服务端多次接收后面接收的没内容
    
    import socket
    
    sever = socket.socket()
    
    sever.bind(
        ('127.0.0.1', 9000)
    )
    
    sever.listen(5)
    
    coon, addr = sever.accept()
    
    data = coon.recv(10)
    print(data)
    
    data = coon.recv(1024)
    print(data)
    
    data = coon.recv(1024)
    print(data)
    >>>b'hellohello'
    >>>b'hello'
    >>>b''
    
    
    - client
    #问题二
    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    client.send(b'hello')
    client.send(b'hello')
    client.send(b'hello')
    

    五、解决粘包问题

    • 演示
    - sever
    
    import socket
    import subprocess
    import struct
    
    sever = socket.socket()
    
    sever.bind(
        ('127.0.0.1', 9000)
    )
    
    sever.listen(5)
    
    while True:
        coon, addr = sever.accept()
        print(addr)
    
        while True:
            try:
    
                #获取客户端传过来的报头
                header = coon.recv(4)
    
                #解包获取真实的数据长度
                data_len = struct.unpack('i', header)[0]
    
                #准备接收真实数据
                cmd = coon.recv(data_len)
    
                if len(cmd) == 0:
                    continue
    
                cmd = cmd.decode('utf-8')
    
                if cmd == 'q':
                    break
    
                #调用subprocess连接终端,对终端进行操作,并获取操作后的正确或错误的结果
                obj = subprocess.Popen(
                    cmd, shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
    
                #结果交给result变量名
                result = obj.stdout.read() + obj.stderr.read()
    
                print('发送给客户端的真实长度', len(result))
    
                #将结果返回给客户端,做一个报头,返回给客户端
                header = struct.pack('i', len(result))
                print(len(header))
                coon.send(header)
                coon.send(result)
    
            except Exception as e:
                print(e)
                break
    
        coon.close()
        
        
    - client
    
    
    import struct
    import socket
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    while True:
    
        cmd = input('客户端输入的内容:')
    
        cmd_bytes = cmd.encode('utf-8')
    
        #做一个报头
        header = struct.pack('i', len(cmd_bytes))
    
        print(len(header))
    
        client.send(header)
    
        #待服务器确认长度以后,再发送真实数据长度
        client.send(cmd_bytes)
    
        #接收服务端返回的报头
        s_header = client.recv(4)
    
        #解包,接收服务端返回的真实数据
        data_len = struct.unpack('i', s_header)[0]
    
        result = client.recv(data_len)
    
        print('接收服务器返回的真实数据长度', len(result))
        print(result.decode('gbk'))
    
    
    • 演示二
    -sever
    
    import socket
    import struct
    import json
    
    sever = socket.socket()
    
    sever.bind(
        ('127.0.0.1', 9000)
    )
    
    sever.listen(5)
    
    while True:
    
        coon, addr = sever.accept()
        print(addr)
    
        while True:
            try:
                #获取客户端传来的报头
                header = coon.recv(4)
    
                #解包,获取真实的数据长度
                json_len = struct.unpack('i', header)[0]
    
                #接收json(dictionary)的真实长度
                json_bytes_data = coon.recv(json_len)
    
                #反序列化将bytes类型数据变成json数据
                json_data = json_bytes_data.decode('utf-8')
    
                back_dic = json.loads(json_data)
                print(back_dic)
                print(back_dic.get('movie_len'))
    
            except Exception as e:
                print(e)
                break
    
        coon.close()
        
    >>>('127.0.0.1', 53414)
    >>>{'movie_name': '色戒', 'movie_len': 100000}
    >>>100000
    
    
    -client
    import struct
    import socket
    import json
    
    client = socket.socket()
    
    client.connect(
        ('127.0.0.1', 9000)
    )
    
    while True:
    
        movie_name = input('请输入上传电影的名字:').strip()
    
        #伪装电影真实数据
        movie_len = 100000
    
        send_dic = {
            'movie_name': movie_name,
            'movie_len': movie_len
        }
    
        #序列化
        json = json.dumps(send_dic)
        # print(json)
        # print(json.encode('utf-8'))
        # print(len(json.encode('utf-8')))
    
        json_bytes = json.encode('utf-8')
    
        #做一个报头
        header = struct.pack('i', len(json_bytes))
    
        #发送报头
        client.send(header)
    
        #发送真实数据
        client.send(json_bytes)
    
    >>>请输入上传电影的名字:色戒
    
  • 相关阅读:
    .netcore持续集成测试篇之Xunit结合netcore内存服务器发送post请求
    .netcore持续集成测试篇之搭建内存服务器进行集成测试一
    .netcore持续集成测试篇之Xunit数据驱动测试
    .netcore持续集成测试篇之开篇简介及Xunit基本使用
    .net持续集成测试篇之Nunit 测试配置
    .net持续集成测试篇之Nunit参数化测试
    .net持续集成测试篇之Nunit that断言
    .net持续集成测试篇之Nunit文件断言、字符串断言及集合断言
    .net持续集成测试篇之Nunit常见断言
    .net持续集成单元测试篇之单元测试简介以及在visual studio中配置Nunit使用环境
  • 原文地址:https://www.cnblogs.com/yafeng666/p/11991720.html
Copyright © 2011-2022 走看看