• 高大上版解决粘包问题


    recv工作原理

    验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。

    server : 按照两个两个这样走

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    conn,client_addr = phone.accept()
    from_client_data1 = conn.recv(2)
    print(from_client_data1)
    from_client_data2 = conn.recv(2)
    print(from_client_data2)
    from_client_data3 = conn.recv(1)
    print(from_client_data3)
    conn.close()
    phone.close()
    '''
    b'he'
    b'll'
    b'o'
    
    '''
    

    client

    import socket
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    phone.send('hello'.encode('utf-8'))
    phone.close()
    
    验证服务端缓冲区取完了,又执行了 recv 操作,此时客户端 20 秒不关闭的前提下,recv 处于阻塞状态

    serve

    import socket
    phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    conn, client_addr = phone.accept()
    from_client_data = conn.recv(1024)
    print(from_client_data)
    print(111)
    conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭。
    print(222)
    
    conn.close()
    phone.close()
    '''
    b'hello'
    111
    222
    '''
    

    client

    import socket
    import time
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    phone.send('hello'.encode('utf-8'))
    time.sleep(20)
    
    验证服务端缓冲区取完了,又执行了recv,此时客户端处于关闭状态,则 recv会取到空字符串

    server

    import socket
    phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    conn, client_addr = phone.accept()
    from_client_data1 = conn.recv(1024)
    print(from_client_data1)
    from_client_data2 = conn.recv(1024)
    print(from_client_data2)
    from_client_data3 = conn.recv(1024)
    print(from_client_data3)
    conn.close()
    phone.close()
    
    '''
    b'hello'
    b''
    b''
    '''
    

    client

    import socket
    import time
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    phone.send('hello'.encode('utf-8'))
    phone.close()
    

    高大上版解决粘包问题

    1. 高大上版: 自定制报头
    
    dic = {'filename': XX, 'md5': 654654676576776, 'total_size': 26743}
    
    2. 高大上版:可以解决文件过大的问题.
    

    server

    import socket
    import subprocess
    import struct
    import json
    phone = socket.socket()
    
    phone.bind(('127.0.0.1',8848))
    
    phone.listen(2)
    # listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
    
    while 1:
        conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
        # print(f'链接来了: {conn,addr}')
    
        while 1:
            try:
    
                from_client_data = conn.recv(1024)  # 接收命令
    
    
                if from_client_data.upper() == b'Q':
                    print('客户端正常退出聊天了')
                    break
    
                obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
    
                                       )
                result = obj.stdout.read() + obj.stderr.read()
                total_size = len(result)
    
                # 1. 自定义报头
                head_dic = {
                    'file_name': 'test1',
                    'md5': 6567657678678,
                    'total_size': total_size,
    
                }
                # 2. json形式的报头
                head_dic_json = json.dumps(head_dic)
    
                # 3. bytes形式报头
                head_dic_json_bytes = head_dic_json.encode('utf-8')
    
                # 4. 获取bytes形式的报头的总字节数
                len_head_dic_json_bytes = len(head_dic_json_bytes)
    
                # 5. 将不固定的int总字节数变成固定长度的4个字节
                four_head_bytes = struct.pack('i',len_head_dic_json_bytes)
    
                # 6. 发送固定的4个字节
                conn.send(four_head_bytes)
    
                # 7. 发送报头数据
                conn.send(head_dic_json_bytes)
    
                # 8. 发送总数据
                conn.send(result)
    
            except ConnectionResetError:
                print('客户端链接中断了')
                break
        conn.close()
    phone.close()
    

    client

    import socket
    import struct
    import json
    phone = socket.socket()
    phone.connect(('127.0.0.1',8848))
    
    while 1:
        to_server_data = input('>>>输入q或者 Q 退出').strip().encode('utf-8')
        if not to_server_data:
            # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以
            # 无论哪一方发送内容时,都不能为空发送
            print('发送内容不能为空')
            continue
        phone.send(to_server_data)
        if to_server_data.upper() == b"Q":
            break
        # 1.接收固定长度的 4 个字节
        head_bytes = phone.recv(4)
        # 2.获得 bytes 类型字典的总字节数
        len_head_dic_json_bytes = struct.unpack('i',head_bytes)[0]
        # 3.接收 bytes 形式的 dic数据
        head_dic_json_bytes = phone.recv(len_head_dic_json_bytes)
        # 4.转化为json 类型 dic
        head_dic_json = head_dic_json_bytes.decode('utf-8')
        # 5.转化为字典形式的报头
        head_dic = json.loads(head_dic_json)
        '''
        head = {
        'file_name': 'test1',
        'md5':2345778832434,
        'total_size':total_size
        }
        '''
        total_data = b''
        while len(total_data) < head_dic['total_size']:
            total_data += phone.recv(1024)
        print(total_data.decode('utf-8'))
    phone.close()
    

    基于udp协议的socket 通信

    server

    # 1. 基于udp协议的socket无须建立管道,先开启服务端或者客户端都行.
    
    # 2. 基于udp协议的socket接收一个消息,与发送一个消息都是无连接的.
    
    # 3. 只要拿到我的ip地址和端口就都可以给我发消息,我按照顺序接收消息.
    
    import socket
    server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    # 基于网络的UDP协议的socket
    server.bind(('127.0.0.1',9000))
    
    while 1:
    
        from_client_data = server.recvfrom(1024)  # 阻塞,等待客户来消息
        print(f'33[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} 33[0m')
        to_client_data = input('>>>').strip()
        server.sendto(to_client_data.encode('utf-8'),from_client_data[1])
    

    client

    import socket
    client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    # 基于网络的UDP协议的socket
    
    while 1:
    
        to_server_data = input('>>>:').strip()
        client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',9000))
        # data,addr = client.recvfrom(1024)
        # print(f'来自服务端{addr}消息:{data.decode("utf-8")}')
    
  • 相关阅读:
    马哥Linux学习笔记之三——加密
    马哥Linux学习笔记之二——网络基础
    网络指标 processer
    上线文切换context switches/sec
    cup利用率和load average的关系
    devexpress 的combobox怎样只能选择不能输入
    c#操作xml
    c# 数据表DataTable给devexpress的gridControl提供数据源
    c#在panel或groupbox中添加窗体,实现点击不同按钮或combox时panel中窗体切换,在xtratabcontrol中添加窗体
    c# 得到list符合某条件的索引值,排序
  • 原文地址:https://www.cnblogs.com/hualibokeyuan/p/11366702.html
Copyright © 2020-2023  润新知