粘包

粘包就是recv(buffer_size)时每次只收取1024字节,如果信息过大,就会积累到下次收取产生粘包

只有基于TCP的套接字有粘包现象
基于UDP的套接字没有粘包现象
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的.

TCP(传输控制协议),是可靠传输

是面向连接的,面向流的,提供高可靠性服务.收发段都要有一对一承兑的socket,因此,发送端为了将发往接收端的包,更有效的发送到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包.这样,接收端就难分辨出来了,必须提供科学的拆包机制.即面向流的通信是无消息保护边界的.

UDP(用户数据报协议),是不可靠传输

是无连接的,面向消息的,提供高效率服务.不会使用块的合并优化算法,由于UDP是支持一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理 了.即面向消息的通信是由消息保护边界的.

tcp是基于数据流的,余数收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是输入空内容,也不是空消息,udp协议会帮你封装上消息头.

send()和recv()不是一一对应关系

当多次发送的信息数据过小时间间隔过段,或发送端信息一次信息过大接收段字节限制过小,都会产生粘包现象

sendto()和recvfrom()是一一对应关系

解决socket的粘包问题

服务端:

#!/usr/bin/env python
from socket import *
import subprocess
import json
import struct

ip_port = ('127.0.0.1',8080)
buffer_size = 1024
back_log = 5
filename = 'malin'

tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)  #防止出现IP地址被占用的错误
tcp_server.bind(ip_port)
tcp_server.listen(back_log)             


while True:
    print('服务器运行了')
    conn,addr = tcp_server.accept()
    while True:
        try:
            cmd = conn.recv(buffer_size).decode('utf8')
            if not cmd:
                break
            res = subprocess.Popen(cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE) #解析命令
            res_err = res.stderr.read()   #读取错误管道的结果
            res_out = res.stdout.read()   #读取标准输出管道的结果
            res_cd = '执行完成'.encode('gbk')
            if not res_err and not res_out:  #管道内都为空时
                length = len(res_cd)
            else:
                length = len(res_err)+len(res_out)
            header_dic = {'filename':filename,'filesize':length}    #报头
            header_json = json.dumps(header_dic).encode('gbk')  #报头序列化,然后编码
            header_len = len(header_json)       #报头长度

            conn.send(struct.pack('i', header_len))  #报头长度打包,发送固定4字节
            conn.send(header_json)     #发送报头
            if not res_err and not res_out:
                conn.send(res_cd)     #当管道内都为空时发送
            else:                     
                conn.send(res_err)    #发送标准错误管道内容
                conn.send(res_out)     #发送标准输出管道内容
        except Exception as e:
            print(e)
            break
    conn.close()

客户端:

#!/usr/bin/env python

from socket import *
import struct
import json
from functools import partial

ip_port = ('127.0.0.1',8080)
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)     #连接服务端

while True:    #通讯循环
    cmd = input('>>:').strip()  #输入CMD命令
    if not cmd:      #命令为空时重新输入
        continue
    if cmd == 'quit':   #正常退出
        break
    tcp_client.send(cmd.encode('utf8'))   #发送命令内容
    head_len_struck = tcp_client.recv(4)   #接收打包后的报头长度
    head_len = struct.unpack('i',head_len_struck)[0]   #解包获取报头长度
    head_json = tcp_client.recv(head_len)    #根据报头长度接收报头
    header = json.loads(head_json.decode('gbk'))   #报头反序列化
    print('报头:',header)
    file_size = header['filesize']   #获取报头里面的文件长度信息
    data = b''   
    i = 0
    while i < file_size:   #根据文件长度,循环收取文件
        data += tcp_client.recv(buffer_size)
        i += buffer_size
    print(data.decode('gbk'))
tcp_client.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值