python-基于tcp协议的套接字(加强版)及粘包问题
一、基于tcp协议的套接字(通信循环+链接循环)
服务端应该遵循:
1.绑定一个固定的ip和port
2.一直对外提供服务,稳定运行
3.能够支持并发
基础版套接字:
from socket import * server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5) conn, client_addr = server.accept() # 通信循环
while True:
data = conn.recv(1024)
conn.send(data.upper()) conn.close()
server.close()
server
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080)) # 通信循环
while True:
msg=input('>>: ').strip()
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data) client.close()
client
以上的程序存在两个bug
1.当客户端单方面终止程序时,服务端抛出异常(linux可以用判断是否为空来处理)
2.recv收到空时,一直在等待。
解决方法:
1.异常捕获
2.再输入时进行判断
改进版:
from socket import * server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8082))
server.listen(5) conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break # 针对linux系统
print('-->收到客户端的消息: ',data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
server.close()
server
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
msg=input('>>: ').strip() #msg=''
if len(msg) == 0:continue
client.send(msg.encode('utf-8')) #client.send(b'')
# print('has send')
data=client.recv(1024)
# print('has recv')
print(data) client.close()
client
链接循环:服务器改进
from socket import * server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8082))
server.listen(5) conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
data = conn.recv(1024)
if len(data) == 0:break # 针对linux系统
print('-->收到客户端的消息: ',data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
server.close()
server
模拟ssh实现远程执行命令
from socket import *
import subprocess server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5) # 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
cmd = conn.recv(1024) #cmd=b'dir'
if len(cmd) == 0: break # 针对linux系统
obj=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
print(len(stdout) + len(stderr))
conn.send(stdout+stderr)
except ConnectionResetError:
break conn.close() server.close()
server
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
cmd_res=client.recv(1024000)
print(cmd_res.decode('gbk')) client.close()
client
recv其实是和本地计算机要数据,所以解码的时候应该用gbk格式
二、粘包
注:只有tcp存在粘包现象,udp永远不存在粘包。
tcp是面向流的协议,发送端可以1k,1k的发送数据,而接收端可以2k,2k的提取数据,发送方往往收集到足够多的数据后才生成一个tcp段,若连续几次需要发送的数据都很少,通常tcp会根据(Nagle)优化算法,把这些数据合成一个TCP段后发出去,这样接收方就收到了粘包数据。
总的来说,粘包问题就是因为接收方不知道消息之间的界限,不知道一次性提取多少字节造成的。
解决方法:(服务端)为字节流加上一个报头,将这个报头(字典形式)json序列化,编码。然后用struct发送报头的长度,再发送报头,最后发送真实数据
(客户端)先解出报头的长度(struct),再接收报头,将拿到的报头解码再反序列化,得到报头字典,最后接收真正的数据
# 服务端必须满足至少三点:
# 1. 绑定一个固定的ip和port
# 2. 一直对外提供服务,稳定运行
# 3. 能够支持并发
from socket import *
import subprocess
import struct
import json server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5) # 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr) # 通信循环
while True:
try:
cmd = conn.recv(1024) # cmd=b'dir'
if len(cmd) == 0: break # 针对linux系统
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 1. 先制作报头
header_dic = {
'filename': 'a.txt',
'md5': 'asdfasdf123123x1',
'total_size': len(stdout) + len(stderr)
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8') # 2. 先发送4个bytes(包含报头的长度)
conn.send(struct.pack('i', len(header_bytes)))
# 3 再发送报头
conn.send(header_bytes) # 4. 最后发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break conn.close() server.close()
server
from socket import *
import struct
import json client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081)) # 通信循环
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1. 先收4bytes,解出报头的长度
header_size=struct.unpack('i',client.recv(4))[0] #2. 再接收报头,拿到header_dic
header_bytes=client.recv(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic['total_size'] #3. 接收真正的数据
cmd_res=b''
recv_size=0
while recv_size < total_size:
data=client.recv(1024)
recv_size+=len(data)
cmd_res+=data print(cmd_res.decode('gbk')) client.close()
client
补充:struct模块
该模块可以帮一个类型,如数字,转成固定长度的bytes
1、 struct.pack
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。其函数原型为:struct.pack(fmt, v1, v2, ...),参数fmt是格式字符串,关于格式字符串的相关信息在下面有所介绍。v1, v2, ...表示要转换的python值。
2、 struct.unpack
struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。
import struct obj1=struct.pack('i',13321111)
print(obj1,len(obj1))#b'\x97C\xcb\x00' 4 res1=struct.unpack('i',obj1)
print(res1[0])#
python-基于tcp协议的套接字(加强版)及粘包问题的更多相关文章
- python 之 网络编程(基于TCP协议的套接字通信操作)
第八章网络编程 8.1 基于TCP协议的套接字通信 服务端套接字函数 s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连 ...
- 网络编程----socket介绍、基于tcp协议的套接字实现、基于udp协议的套接字实现
一.客户端/服务器架构(C/S架构) 即C/S架构,包括: 1.硬件C/S架构(打印机) 2.软件C/S架 ...
- 网络编程(二)--TCP协议、基于tcp协议的套接字socket
一.TCP协议(Transmission Control Protocol 传输控制协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会 ...
- 网络编程之TCP三次握手与四次挥手、基于TCP协议的套接字编程
目录 TCP三次握手和四次挥手 背景描述 常用的熟知端口号 TCP概述 TCP连接的建立(三次握手) TCP四次挥手 如果已建立连接,客户端突然断开,会怎么办呢? 基于TCP协议的套接字编程 什么是S ...
- 什么是 socket?简述基于 tcp 协议的套接字通信流程?
Socket的英文原义是"孔"或"插座".通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄, 可以用来实现不同虚拟机或不同计 ...
- 网络编程(二)——TCP协议、基于tcp协议的套接字socket
TCP协议与基于tcp协议的套接字socket 一.TCP协议(流式协议) 1.可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的 ...
- [网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序]
[网络编程之Socket套接字介绍,套接字工作流程,基于TCP协议的套接字程序] 为何学习socket套接字一定要先学习互联网协议: 1.首先:要想开发一款自己的C/S架构软件,就必须掌握socket ...
- 01:osi七层---基于TCP协议的套接字(socket)
1 : osi 七层,tcp/ip 五层 1 cs架构和bs架构2 互联网3 osi七层 tcp/ip五层 -物理层 -网线.光纤 -数据链路层 -网卡 - ...
- 基于TCP协议的套接字编程
06.26自我总结 1.关于Socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在 ...
- 网络编程[第一篇]基于tcp协议的套接字编程
将服务端-客户端的连接比作双方打电话的过程 2019-07-24 一.客户端 主动的一方: 客户端实例化一个socket对象--> 主动像服务端发送连接请求--> (服务端接受请求后即可进 ...
随机推荐
- PowerDesigner 批量添加字段
Option Explicit Dim mdl ' the current model Set mdl = ActiveModel Dim Tab 'running table Dim col_1 D ...
- 使用systemback制作Ubuntu自定义系统镜像和系统备份(抄)
使用systemback制作Ubuntu自定义系统镜像和系统备份 2017年06月23日 16:17:51 BWBOT 阅读数:10714 原链接:https://community.bwbot. ...
- java回调函数,看完就懂
java回调函数在网上了看了些例子,比较绕,不够清晰,自己写的一个例子比较通俗,java回调其实很简单. 举个例子我是类B,我有个方法叫b(),现在我要调用类A中的方法a(),写个代码就是: publ ...
- ImageMagick 安装 window10与错误总结
一般默认安装最新的版本(目前是7.0.8),但是通过测试发现这个版本会报错(ImportError: MagickWand shared library not found.)所以建议安装 im6版本 ...
- 浅谈vuex使用方法(vuex简单实用方法)
Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vu ...
- BlockQueue 解析
生产者.消费者模式 https://www.jianshu.com/p/024a36b83099
- 洛谷 P4168 [Violet] 蒲公英
历尽千辛万苦终于AC了这道题目... 我们考虑1个区间\([l,r]\), 被其完整包含的块的区间为\([L,R]\) 那么众数的来源? 1.\([l,L)\)或\((R,r]\)中出现的数字 2.\ ...
- 集合源码分析[2]-AbstractList 源码分析
AbstractList 类型:抽象类 接口的继承以及实现关系 继承AbstractCollection 实现List接口 典型方法实现解析 public List<E> subList( ...
- 洛谷P2604 网络扩容 拆点+费用流
原题链接 这题貌似比较水吧,最简单的拆点,直接上代码了. #include <bits/stdc++.h> using namespace std; #define N 1000 #def ...
- 3194. 【HNOI模拟题】化学(无标号无根树计数)
Problem 求\(n\)个点的每个点度数不超过\(4\)的无标号无根树个数. Data constraint \(1\le n\le 500\) Solution 尝试着把问题一般化.我们来考虑一 ...