subproess模块

import subprocess

while True:
cmd = input('cmd>>>: ')
if cmd == 'q':
break data = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE, # 返回标准输出结果
stderr=subprocess.PIPE # 返回标准错误结果
) res = data.stdout.read() + data.stderr.read()
print(res.decode('gbk'))

TCP粘包问题

服务端第一次发送的数据,客户端无法精确一次性接收完毕,下一次发送的数据与上一次数据黏在一起了。

  1. 无法预测对方需要接收的数据大小长度
  2. TCP流式协议,会将多次连续发送数据量小、并且时间间隔短的数据一次性打包发送。

基于TCP的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始何处结束。

粘包两种情况

  1. 服务端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
# server.py

import socket

server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5)
conn, addr = server.accept() data1 = conn.recv(1024)
data2 = conn.recv(1024)
data3 = conn.recv(1024)
print(data1)
print(data2)
print(data3) # client.py import socket client = socket.socket()
client.connect(
('127.0.0.1', 9527)
) client.send(b'hello')
client.send(b'hello')
client.send(b'hello') client.close() '''
b'hellohellohello'
b''
b''
'''
  1. 客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据
# 服务端
import socket server = socket.socket()
server.bind(
('127.0.0.1', 9527)
) server.listen(5)
conn, addr = server.accept() data1 = conn.recv(2)
data2 = conn.recv(20) print(data1)
print(data2) # 客户端
import socket client = socket.socket()
client.connect(
('127.0.0.1', 9527)
) client.send(b'hello world and shcaondskasd nknasksfn km') client.close()
'''
b'he'
b'llo world and shcaon
'''

解决粘包问题

使用struct模块

struct模块是一个可以将很长的数据的长度,压缩成固定的长度的一个标记(数据报头)。

必须先定义报头,发送报头,再发送真实数据

struct模块的使用

import struct
str1 = '123a56' # 打包
# i模式会将数据长度压缩成4个bytes
headers = struct.pack('i', len(str1))
print(headers)
print(len(headers)) # 解包
data_len = struct.unpack('i', headers)
print(data_len[0]) # 真实数据长度为len(str1)

使用struct模块解决粘包

服务端:

import struct
import socket
import subprocess server = socket.socket()
server.bind(
('127.0.0.1', 8888)
) server.listen(5)
while True:
conn, addr = server.accept()
print(addr)
while True:
try:
cmd = conn.recv(1024).decode('utf-8')
if len(cmd) == 0:
continue
if cmd == 'q':
break
res = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
data = res.stdout.read() + res.stderr.read()
# 打包压缩
headers = struct.pack('i', len(data))
# 先发送头部
conn.send(headers)
# 再发送真实数据
conn.send(data) except Exception:
break conn.close()

客户端:

import struct
import socket client = socket.socket()
client.connect(
('127.0.0.1', 8888)
) while True:
cmd = input('cmd>>>:')
client.send(cmd.encode('utf-8'))
if cmd == 'q':
break # 先获取数据报头
headers = client.recv(4)
# 再解包获取真实数据
data_len = struct.unpack('i', headers)[0]
# 接收真实数据长度
data = client.recv(data_len) print(data.decode('gbk')) client.close()

优化解决粘包问题

把数据真实长度和数据的描述信息一同发送过去

服务端:

import socket
import struct
import json server = socket.socket()
server.bind(('127.0.0.1', 9999)) server.listen(5) while True:
conn, addr = server.accept()
print(addr) while True:
try:
# 接收数据报头
headers = conn.recv(4) # 解包获取真实数据长度
data_len = struct.unpack('i', headers)[0] bytes_data = conn.recv(data_len)
back_dic = json.loads(bytes_data.decode('utf-8'))
print(back_dic) except Exception:
break conn.close()

客户端:

import socket
import struct
import json client = socket.socket()
client.connect(('127.0.0.1', 9999)) while True:
dic = {
'file_name': 'shoot on me',
'file_size': 10000000
} # json序列化
json_data = json.dumps(dic)
json_bytes = json_data.encode('utf-8') # 压缩
headers = struct.pack('i', len(json_bytes)) client.send(headers)
client.send(json_bytes)

上传大文件

服务端

import socket
import struct
import json server = socket.socket()
server.bind(('127.0.0.1', 9999)) server.listen(5) conn, addr = server.accept()
print(addr) while True:
try:
headers = conn.recv(4)
data_len = struct.unpack('i', headers)[0]
bytes_data = conn.recv(data_len) back_dic = json.loads(bytes_data.decode('utf-8'))
print(back_dic) file_name = back_dic.get('file_name')
file_size = back_dic.get('file_size') init_data = 0
# 写入文件视频
with open(file_name, 'wb') as f:
while init_data < file_size:
data = conn.recv(1024)
f.write(data)
init_data += len(data)
print(f'{file_name}接收完毕! ')
except Exception as e:
print(e)
break conn.close()

客户端

import socket
import struct
import json client = socket.socket()
client.connect(('127.0.0.1', 9999)) # 1.打开视频文件,获取视频数据
with open(r'D:\pycharm_project\忌日快乐2.mkv', 'rb') as f:
movie_bytes = f.read()
# 2.为视频组织一个字典
send_dic = {
'file_name': '忌日快乐2.mkv',
'file_size': len(movie_bytes)
}
# 3.先打包字典,发送headers,再发字典真实数据
json_data = json.dumps(send_dic)
bytes_data = json_data.encode('utf-8')
headers = struct.pack('i', len(bytes_data))
client.send(headers)
client.send(bytes_data)
# 再发真实视频数据
init_data = 0
num = 1
with open(r'D:\pycharm_project\忌日快乐2.mkv', 'rb') as f:
while init_data < len(movie_bytes):
send_data = f.read(1024)
print(send_data, num)
num += 1
client.send(send_data)
init_data += len(send_data)

UDP协议

UDP是一种传输协议

  1. 不需要建立双向通道
  2. 不会粘包
  3. 客户端给服务端发送数据,不需要等待服务返回接收成功

upd套接字

# server.py
import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(
('127.0.0.1', 8888)
) msg, addr = server.recvfrom(1024)
msg1, addr1 = server.recvfrom(1024)
msg2, addr2 = server.recvfrom(1024)
msg3, addr3= server.recvfrom(1024)
print(msg)
print(msg1)
print(msg2)
print(msg3) # client.py
import socket client = socket.socket(type=socket.SOCK_DGRAM) client.sendto(b'hello', ('127.0.0.1', 8888))
client.sendto(b'hello', ('127.0.0.1', 8888))
client.sendto(b'hello', ('127.0.0.1', 8888))
client.sendto(b'hello', ('127.0.0.1', 8888)) '''
b'hello'
b'hello'
b'hello'
b'hello'
'''

基于upd实现qq聊天室

# server.py
import socket server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8888)) while True:
msg, addr = server.recvfrom(1024)
print(addr)
print(msg.decode('utf-8')) send_msg = input('服务端发送消息:').encode('utf-8')
server.sendto(send_msg, addr) # client.py
import socket client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1', 8888) while True:
send_msg = input('客户端1:').encode('utf-8')
client.sendto(send_msg, server_ip_port) msg, addr = client.recvfrom(1024)
print(msg.decode('utf-8'))

socketserver

python内置模块,可以简化socket套接字服务端的代码,必须要创建一个类

# server.py
import socketserver class MyTCPServer(socketserver.BaseRequestHandler):
# 必须要重写父类的handler
def handle(self):
print(self.client_address) while True:
try:
data = self.request.recv(1024).decode('utf-8')
send_msg = data.upper()
self.request.send(send_msg.encode('utf-8')) except Exception:
break if __name__ == '__main__':
server = socketserver.ThreadingTCPServer (
('127.0.0.1', 8888), MyTCPServer
)
server.serve_forever() # client.py
import socket client = socket.socket()
client.connect(
('127.0.0.1', 8888)
) while True:
send_msg = input('客户端:')
if send_msg == 'q':
break
client.send(send_msg.encode('utf-8')) data = client.recv(1024).decode('utf-8')
print(data) client.close()

tcp粘包、解决粘包问题的更多相关文章

  1. Python进阶----粘包,解决粘包(旗舰版)

    Python进阶----粘包,解决粘包(旗舰版) 一丶粘包 只有TCP有粘包现象,UDP永远不会粘包 什么是粘包     存在于客户端接收数据时,不能一次性收取全部缓冲区中的数据.当下一次再有数据来时 ...

  2. tcp套接字粘包解决办法

    粘包只会出现在tcp,udp传输不会产生粘包现象.解决粘包的原理就是服务器预先向客户端发送客户端即将获取文件的大小. 第一版解决方案: 服务器: # Author : Kelvin # Date : ...

  3. socket编程 TCP 粘包和半包 的问题及解决办法

    一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次 ...

  4. 8-2udp和tcp网络编程以及粘包和解决粘包的方法

    一  tcp网络编程 server 端 import socket sk=socket.socket() #实例化一个对象 sk.setsockopt(socket.SOL_SOCKET,socket ...

  5. TCP粘包问题的解决方案02——利用readline函数解决粘包问题

      主要内容: 1.read,write 与 recv,send函数. recv函数只能用于套接口IO ssize_t recv(int sockfd,void * buff,size_t len,i ...

  6. 《连载 | 物联网框架ServerSuperIO教程》- 9. 协议过滤器,解决一包多发、粘包、冗余数据

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  7. c# socket 解决粘包,半包

    处理原理: 半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度时,将消息临时缓存起来,直到满足长度再解码 粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然后再判断 ...

  8. 关于TCP封包、粘包、半包

    关于Tcp封包 很多朋友已经对此作了不少研究,也花费不少心血编写了实现代码和blog文档.当然也充斥着一些各式的评论,自己看了一下,总结一些心得. 首先我们学习一下这些朋友的心得,他们是: http: ...

  9. python3全栈开发-什么是粘包、粘包现象、如何解决粘包

    一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.decode( ...

随机推荐

  1. 项目中使用http referer,为了盗取图片资源

    项目背景:因为图片的数据是爬取的别人的图片,而且保存的数据仅仅是图片地址链接,为了减少数据存储和服务器压力,但是这就引发一个问题,有的图片地址没有做防盗处理,可以随意的下载使用:但有些图片的服务器做了 ...

  2. socket调试工具(Mac版)

    基于Mac版的Socket测试功能,类似于PostMan的功能,对于Socket长链接的项目开发很有帮助. 本人也是通过好多渠道才找到这篇文章,与大家共享: 按照步骤一步一步来就对了~ 本文参考于:h ...

  3. python小技巧 小知识

    python小技巧 小知识 python系统变量(修改调用shell命令路径)或用户空间说明 20150418 python调用系统命令,报找不到.怎么办? 类似执行shell的: [ -f /etc ...

  4. 树莓派Raspberry pi安装系统/烧录系统

    一:下载系统文件 1.树莓派官网系统下载链接:https://www.raspberrypi.org/downloads/raspbian/ (也可在百度云盘下载:https://pan.baidu. ...

  5. 安装docker并部署web项目

    一.docker简介 1.docker定义:docker是一个用来装应用的容器,就像杯子可以装水,笔筒可以装笔,书包可以放书一样.你可以把“Hello World!”放到docker中,也可以把网站放 ...

  6. Python—创建进程的三种方式

    方式一:os.fork() 子进程是从os.fork得到的值,然后赋值开始执行的.即子进程不执行os.fork,从得到的值开始执行. 父进程中fork之前的内容子进程同样会复制,但父子进程空间独立,f ...

  7. Linux-3.14.12内存管理笔记【建立内核页表(1)】

    前面已经分析过了Intel的内存映射和linux的基本使用情况,已知head_32.S仅是建立临时页表,内核还是要建立内核页表,做到全面映射的.下面就基于RAM大于896MB,而小于4GB ,切CON ...

  8. C++ 虚函数的说明

    虚函数的几点说明: 1. 当一个成员函数定义为虚函数时,其派生类中的同名函数也自动为虚函数.无论其是否添加了 virtual 关键字. 为了能良好的阅读代码,请加上. 2. 父类的虚函数,就是为了让子 ...

  9. 新手入门:python的pip安装(二)

    pip的安装以及使用pip安装包 —–安装python的时候勾选了下载pip,不知道为什么没下载.然后就偷懒想着需要哪个包再单独去下载就好了,然后!!!每个包都会出点小问题,导致我这个初学者有三天不想 ...

  10. 深入理解ES6之解构

    变量赋值的痛 对象 let o = {a:23,b:34}; let a = o.a; let b = o.b; 如上文代码,我们经常会遇到在各种场合需要获取对象中的值的场景,舒服一点的是获取单个属性 ...