解决客户端与服务端传输数据黏包问题

黏包原因

1.因为发送数据包时,每次发送的包小,因为系统进行优化算法,就将两次的包放在一起发送,减少了资源的重复占用。多次发送会经历多次网络延迟,一起发送会减少网络延迟的次数。因此在发送小数据时会将两次数据一起发送,而客户端接收时,则会一并接收。#即出现多次send会出现黏包

2.是因为接收数据时,又多次接收,第一次接收的数据量小,导致数据还没接收完,就停下了,剩余的数据会缓存在内存中,然后等到下次接收时和下一波数据一起接收。

方案1

客户端将数据分为两个阶段发给服务端

  1. 将报头内容组成结构体 ,发给服务端;报头:报头标识"package"+报文长度Len
  2. 发送完报头,紧接着发送长度为Len的报文(json串形式)发给服务端;

客户端代码示例

struct pkg_header_t
{
	char cType[8];//package
	unsigned int len;
};

INT64 ClientSocket::SendData(QString qMsg)
{
	 /*send to server*/
 	QTextCodec *codec = QTextCodec::codecForName("utf-8");
	codec->setCodecForLocale(codec);
	QString qMsg = codec->toUnicode(msg.toLocal8Bit());

	qDebug()<<qMsg.size()<<endl;
 	pkg_header_t pkg;
	strncpy(pkg.cType,"package",8);


	QByteArray q = qMsg.toLocal8Bit();
    pkg.len = q.size();
	qDebug()<<q.size()<<endl;

	INT64 iRet = client->write((const char*)((void *)&pkg),sizeof(pkg_header_t));
	if (-1 == iRet)
	{
    	qCritical() << "Message send head error";
	}
 	iRet = client->write(q);
	if (-1 == iRet)
	{
    	qCritical() << "Message send error";
	}
	return iRet;
}

服务端将数据分为两个阶段接收

  1. 接收报头结构体 ,校验报头标识"package",获取报文长度Len
  2. 根据报头信息紧接着循环接收,直到接收长度为Len的报文(json串);

方案2

在这里插入图片描述

服务端代码示例

import json
import socket
import struct
sk =socket.socket()#创建一个socket对象
sk.bind(('127.0.0.1',8080))#绑定本地ip地址与端口
sk.listen()#开启监听
buffer =1024    #设置buffer值大小
conn,addr =sk.accept()#等待客户端连接服务端,得到地址与双共工通道
head_len=conn.recv(4)#接收用struck将数字转长度为4的bytes
head_len =struct.unpack('i',head_len)[0]#调用struct模块来解包,得到原来的数字(数字为报头的长度)
json_head =conn.recv(head_len).decode('utf-8')#接收json序列化的报头进行解码
head =json.loads(json_head)#将json序列化的报头进行反序列化
filesize =head['filesize']#拿到head字典中键filesize所对应的值
print(filesize)#打印filesize
with open(r'dir\%s'%head['filename'],'wb')as f:#dir\文件名,拿到文件的路径,以wb模式打开
    while filesize:#当filesize(文件内剩余内容的大小)有值时
        if filesize >=buffer:#如果filesize>= buffer值,buffer值是设定的一次接收多少字节的内容
            print(filesize)  #打印filesize大小
            content =conn.recv(buffer)#接收buffer值大小的内容
            f.write(content)#写入文件
            filesize -=buffer#原来的文件大小减去接收的内容,等于剩余文件的大小
        else:#如果文件剩余的内容大小<buffer设定的大小,就全部接收
            content =conn.recv(filesize)
            f.write(content)
            filesize =0
        print('=====>',len(content))
    print(filesize)
print('服务器端')
conn.close()
sk.close()

客户端代码示例

import struct
import os
import json
import socket
sk =socket.socket
sk.connect(('127.0.0.1',8090))
buffer =1024
head ={'filepath':r'D:\Documents\oCam',
       'filename':r'test.mp4',
       'filesize':None}#定义一个报头
file_path =os.path.join(head['filepath'],head['filename'])#将文件名与文件路径加载进目录中
filesize = os.path.getsize(file_path)#得到目录中文件的大小
head['filesize'] =filesize#将文件大小赋值回列表中
json_head =json.dumps(head)#将head字典序列化
bytes_head =json_head.encode('utf-8')#将序列化之后的字典进行解码
head_len =len(bytes_head)#计算转码之后字典的长度
pack_len =struct.pack('i',head_len)#调用struct模块将长度转换成长度为4的bytes类型
sk.send(pack_len)#发送pack_len
sk.send(bytes_head)#发送bytes_head
with open(file_path,'rb')as f:
    while filesize:
        print(filesize)
        if filesize>=buffer:
            content =f.read(buffer)
            print('====>',len(content))
            sk.send(content)
            filesize-=buffer
        else:
            content =f.read(filesize)
            sk.send(content)
            filesize=0
sk.close()

转自:https://www.cnblogs.com/kakawith/p/8378425.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值