网络编程之-----粘包

一.什么是粘包:

  TCP是可靠的面向连接的协议,传输效率低,全双工通信,面向字节流.

  UDP是不可靠的无连接的协议,传输效率高,无阻塞控制.

  粘包的成因是由于TCP协议本身造成的,TCP为了提高传输效率,发送方往往会收集到足够多的数据才发送一个TCP段.如果连续几次发送的数据都很少,TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样,接收方接收到的数据就发生了粘包.

  发生粘包的两种情况:

    1.由于发送端的缓存机制导致的粘包

      发送端需要等缓存区满以后才发送,造成粘包(发送数据时间间隔短,数据少,就会合到一起,产生粘包.)

import socket
server = socket.socket()
ip_port = ('127.0.0.1', 8000)
server.bind(ip_port)
server.listen(5)
conn,addr = server.accept()
data1 = conn.recv(10)
data2 = conn.recv(10)
print('data1', data1.decode('utf-8'))
print('data2', data2.decode('utf-8'))
conn.close()
服务端
import socket
client = socket.socket()
ip_port = ('127.0.0.1', 8000)
client.connect(ip_port)
client.send('hello'.encode('utf-8'))
client.send('world'.encode('utf-8'))
客户端

    此时服务端收到的消息:(data1:helloworld),(data2: )

    2.由于服务端的缓存机制导致的粘包

      接收方没有及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只接受了一部分,服务端下次接收数据的时候还是从缓存区上次剩下的地方开始拿,产生了粘包.)

import socket
server = socket.socket()
ip_port = ('127.0.0.1', 8000)
server.bind(ip_port)
server.listen(5)
conn,addr = server.accept()
data1 = conn.recv(2)
data2 = conn.recv(10)
print('data1', data1.decode('utf-8'))
print('data2', data2.decode('utf-8'))
conn.close()
服务端
import socket
client = socket.socket()
ip_port = ('127.0.0.1', 8000)
client.connect(ip_port)
client.send('hello'.encode('utf-8'))
client.send('world'.encode('utf-8'))
客户端

    此时服务端收到的消息:(data1:he), (data2:lloworld)

  总结:

    1.粘包只发生在TCP协议中.

    2.粘包不一定会发生

    3.表面上看,粘包问题主要是因为发送方和接收方分缓存机制,TCP面向连接通信的特点产生的.

    4.实际上,主要因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的.

粘包的解决方案

  

import socket
import subprocess
import struct       #struct 可以定制报头
server = socket.socket()
ip_port = ('127.0.0.1', 8018)
server.bind(ip_port)
server.listen(5)
while True:
    print('server is working......')
    conn,addr = server.accept()
    while True:
        try:            #处理客户端断开连接异常
            cmd = conn.recv(1024)
            if cmd == b'exit':
                break
            res = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   )
            err = res.stderr.read()
            if err:
                data = err
            else:
                data = res.stdout.read()
            conn.send(struct.pack('i', len(data)))     #自定制报头
            conn.send(data)                            #发送所有报文
        except Exception as e:
            break
    conn.close()
服务端
import socket
import struct
client = socket.socket()
ip_port = ('127.0.0.1', 8018)
client.connect(ip_port)
while True:
    cmd = input('请输入命令:')
    if cmd == '':
        continue
    elif cmd == 'exit':
        break
    client.send(cmd.encode('utf-8'))

    header_pack = client.recv(4)
    header = struct.unpack('i', header_pack)[0]
    data_length = 0
    data_num = b''
    while data_length < header:
        data = client.recv(1024)
        data_num += data
        data_length += len(data)
    print(data_num.decode('gbk'))
客户端

 

posted @ 2018-09-04 20:44  Virous1887  阅读(146)  评论(0编辑  收藏  举报