高大上版解决粘包问题

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'\033[1;35;0m来自客户端{from_client_data[1]}: {from_client_data[0].decode("utf-8")} \033[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")}')
posted @ 2019-08-16 22:33  挂机兄  阅读(114)  评论(0编辑  收藏  举报