解决黏包的问题

一,黏包的触发

sk.bind(('127.0.0.1',8090))
sk.listen()
conn,addr=sk.accept()
ret=conn.recv(1)
ret1=conn.recv(1)
ret2=conn.recv(1)
print(ret)
print(ret1)
print(ret2)
conn.close()
sk.close()
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8090))
sk.send(b'hello1234')
sk.close()
C:\Users\hc\AppData\Local\Programs\Python\Python36\python3.exe C:/s9/day32/网络编程.py
b'h'
b'e'
b'l'

Process finished with exit code 0

当接受的字节小于发送的字节数,发送一个数据包,可以有多个recv接收,直到接收完了为止

sk.bind(('127.0.0.1',8090))
sk.listen()
conn,addr=sk.accept()
ret=conn.recv(1024)
ret1=conn.recv(2)
ret2=conn.recv(2)
print(ret)
print(ret1)
print(ret2)
conn.close()
sk.close()
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8090))
sk.send(b'h')
sk.send(b'1234')
sk.close()
C:\Users\hc\AppData\Local\Programs\Python\Python36\python3.exe C:/s9/day32/网络编程.py
b'h1234'
b''
b''

Process finished with exit code 0

多个send小数据连在一起,会发生黏包现象,是tcp协议内部的优化算法造成的

在发送端有一个缓存机制

当发送方没有继续发送的时候,接收方还一直有recv在等待接收,此时会返回空的b“   ”的形式

 

解决黏包方法一:

import socket
sk=socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()
conn,addr=sk.accept()
while 1:
    info=input('>>')
    if info=='q':
        conn.send(b'q')
        break
    conn.send(info.encode('gbk'))
    ret1=conn.recv(1024).decode('utf-8')
    print(ret1)
    conn.send(b'ok')
    ret=conn.recv(int(ret1)).decode('gbk')
    print(ret)
conn.close()
sk.close()
import socket,subprocess
sk=socket.socket()
sk.connect(('127.0.0.1',8090))
while 1:
    ret=sk.recv(1024).decode('gbk')
    if ret=='q':
        break
    res=subprocess.Popen(ret,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    a=res.stdout.read()
    b=res.stderr.read()
    sk.send((str(len(a)+len(b)).encode('utf-8')))
    sk.recv(1024)
    sk.send(a)
    sk.send(b)
sk.close()

由于之前是不知道自己发送的数据是多大的,所以现在首先发送过去的是这个数据的大小,然后在按照数据的长度来接收

好处:确定我们到底要接收多大的数据,

当我们要发送大数据的时候 ,要明确的告诉接收方要发送多大的数据,以便接收方能够准确的接收到所有数据
# 多用在文件传输的过程中

缺点:

不好的地方:多了一次交互
方法二:使用模块,struct
import socket,struct

sk=socket.socket()
sk.bind(('127.0.0.1',8090))
sk.listen()
conn,addr=sk.accept()
while 1:
    info=input('>>')
    if info=='q':
        conn.send(b'q')
        break
    conn.send(info.encode('gbk'))
    ret1=conn.recv(4)
    ret1=struct.unpack('i',ret1)[0]
    ret=conn.recv(int(ret1)).decode('gbk')
    print(ret)
conn.close()
sk.close()
import socket,subprocess,struct
sk=socket.socket()
sk.connect(('127.0.0.1',8090))
while 1:
    ret=sk.recv(1024).decode('gbk')
    if ret=='q':
        break
    res=subprocess.Popen(ret,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    a=res.stdout.read()
    b=res.stderr.read()
    len_1=(len(a)+len(b))
    len_1=struct.pack('i',len_1)
    sk.send(len_1)
    sk.send(a)
    sk.send(b)
sk.close()

在网络上传输的所有数据都叫数据包,数据包里的所有数据都叫报文,

报文里有的不止是数据,还有ip地址,mac地址,,端口号

所有的报文都有报头

报文可以是自己定制的一般在比较复杂的时候使用

一般有:

文件的名字,

文件的大小

文件的类型

存储路径

下面写一个用自己设置的报文来写一个下载的程序

client:

import
os import json import struct import socket sk = socket.socket() sk.connect(('127.0.0.1',8090)) buffer = 1024 # buffer = 1024 # 发送文件 head = {'filepath':r'C:\Users\hc\Desktop', 'filename':r'单词表--01.xlsx', '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) # 字典转成了字符串 bytes_head = json_head.encode('utf-8') # 字符串转bytes # 计算head的长度 head_len = len(bytes_head) # 报头的长度 pack_len = struct.pack('i',head_len) sk.send(pack_len) # 先发报头的长度 sk.send(bytes_head) # 再发送bytes类型的报头 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()
server:



import
time import json import socket import struct sk = socket.socket() sk.bind(('127.0.0.1',8090)) sk.listen() buffer = 1024 # buffer = 1024 conn,addr = sk.accept() # 接收 head_len = conn.recv(4) head_len = struct.unpack('i',head_len)[0] json_head = conn.recv(head_len).decode('utf-8') head = json.loads(json_head) filesize = head['filesize'] print(filesize) with open(head['filename'],'wb') as f: while filesize: if filesize >= buffer: print(filesize) content = conn.recv(buffer) f.write(content) filesize -= buffer else: content = conn.recv(filesize) f.write(content) filesize = 0 print('====>',len(content)) print(filesize) print('服务端。。。。') conn.close() sk.close()

 

posted @ 2018-01-29 16:33  许光宗  阅读(149)  评论(0编辑  收藏  举报