一,黏包的触发
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:UsershcAppDataLocalProgramsPythonPython36python3.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:UsershcAppDataLocalProgramsPythonPython36python3.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:UsershcDesktop', '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()