美文网首页
TCP粘包现象以及struct模块

TCP粘包现象以及struct模块

作者: MononokeHime | 来源:发表于2018-09-04 16:03 被阅读0次
image

为什么会出现粘包现象?

是因为TCP协议是面向流的协议,在发送的数据传输的过程中还有缓存机制来避免数据丢失,因此在连续发送小数据的时候,以及接受大小不符的时候都容易出现粘包现象,本质还是因为我们在接受数据的时候不知道发送的数据的长短

情况一 发送方的缓存机制

# server.py
from socket import socket

ser_socket = socket()
ser_socket.bind(('0.0.0.0',8000))
ser_socket.listen()
conn,addr = ser_socket.accept()

data = conn.recv(12)
print(data)
data = conn.recv(12)
print(data)
conn.close()
ser_socket.close()

# client.py
from socket import *

cli_socket = socket()
cli_socket.connect(('127.0.0.1',8000))

cli_socket.send(b'hello')
cli_socket.send(b'world')
cli_socket.close()

我们会发现sever端接受的是b'helloworld'b'',这就是粘包现象,这是由于客户端发送的数据包太小并且是连续的,那么协议栈内部会采用Nagle算法对连续发送的小数据包进行合并然后再发送。如果我们在上述代码的client端两次send之间sleep1秒,则server端会接受到b'hello'b'world'

情况二 接收方的缓存机制

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

# server.py
from socket import socket
import time
ser_socket = socket()
ser_socket.bind(('0.0.0.0',8000))
ser_socket.listen()
conn,addr = ser_socket.accept()

data = conn.recv(2)
print(data)  # b'he'
time.sleep(5)
data = conn.recv(10)
print(data) # b'lloworld'
conn.close()
ser_socket.close()

# client.py
from socket import *

cli_socket = socket()
cli_socket.connect(('127.0.0.1',8000))

cli_socket.send(b'hello')
cli_socket.send(b'world')
cli_socket.close()

客户端很快发送结束了,服务端首先接受2个字节的数据,之后sleep 5秒之后,会继续从缓存里取剩下的数据。

怎么解决粘包问题?
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes


image

通常我们只用到int型,它会将int型整数转成4个字节

>>>import struct
>>>res = struct.pack('i',2048)
>>>res
b'\x00\x08\x00\x00'
>>>len(res)
4
>>>struct.unpack('i',res)
(2048,)  # 元祖

简单的SSH服务示例(server和client倒置了,这不是重点):
server.py

import struct
import socket

sk = socket.socket()
sk.bind(('0.0.0.0',8080))
sk.listen()

conn,addr = sk.accept()

while True:
    cmd = input('>>>')
    if cmd == 'q':
        conn.send(b'q')
        break
    conn.send(cmd.encode('utf-8'))
    num = conn.recv(4) # 确认需要发送的数据长度
    num = struct.unpack('i',num)[0]
    res = conn.recv(int(num)).decode('utf-8')
    print(res)
conn.close()
sk.close()

client.py

import struct
import socket
import subprocess

sk = socket.socket()
sk.connect(('127.0.0.1',8080))

while True:
    cmd = sk.recv(1024).decode('utf-8')
    if cmd == 'q':
        break
    res = subprocess.Popen(cmd,shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE)

    std_out = res.stdout.read()
    std_err = res.stderr.read()
    len_num = len(std_out)+len(std_err)
    num_byte = struct.pack('i',len_num)
    sk.send(num_byte)
    sk.send(std_out)
    sk.send(std_err)
sk.close()

参考

http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5

相关文章

  • TCP粘包现象以及struct模块

    为什么会出现粘包现象? 是因为TCP协议是面向流的协议,在发送的数据传输的过程中还有缓存机制来避免数据丢失,因此在...

  • Socket粘包处理

    什么是粘包 TCP有粘包现象,而UDP不会出现粘包。 TCP(Transport Control Protocol...

  • JAVA-每日一面 2022-01-25

    什么是 TCP 粘包/拆包以及TCP 粘包/拆包的解决办法 TCP 粘包/拆包1、要发送的数据大于 TCP 发送缓...

  • TCP粘包现象

    什么是粘包现象 TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前...

  • tcp远端命令执行--解决粘包

    客户端服务端通信简图 两种粘包现象 解决粘包现象 只有tcp通信存在粘包现象udp会直接产生OSError异常 客...

  • golang 解决 TCP 粘包问题

    什么是 TCP 粘包问题以及为什么会产生 TCP 粘包,本文不加讨论。本文使用 golang 的 bufio.Sc...

  • TCP粘包处理

    TCP粘包 TCP粘包的处理

  • 网络编程-黏包

    注意:只有TCP有粘包现象,UDP永远不会粘包 黏包的原因一: udp接受一个数据包的代码ret, addr = ...

  • TCP粘包和拆包

    TCP的粘包和拆包 粘包和拆包现象 客户端给服务端发送数据可能存在的场景: 1.无拆包粘包 服务端分两次读取到了两...

  • TCP协议下的粘包与拆包,如何解决

    TCP协议下的粘包与拆包,如何解决 TCP协议下的粘包与拆包,如何解决一、粘包、拆包1.1 粘包原因1.1.1 滑...

网友评论

      本文标题:TCP粘包现象以及struct模块

      本文链接:https://www.haomeiwen.com/subject/rmiiwftx.html