返回顶部
首页 > 资讯 > 后端开发 > Python >Python Socket通信黏包问题分
  • 489
分享到

Python Socket通信黏包问题分

通信PythonSocket 2023-01-30 22:01:06 489人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

参考:Http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在tcp协议通信的情况下,才会产生黏包问题 基于TCP协

参考:Http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5

1.黏包的表现(以客户端远程操作服务端命令为例)

注:只有在tcp协议通信的情况下,才会产生黏包问题

基于TCP协议实现的黏包

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.py

import Socket
import subprocess

ip_port    = ('127.0.0.1', 8080) #服务端地址及端口
BUFFERSIZE = 1024 #设置缓冲区大小

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后,重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接

while True:
    conn, addr = tcp_server_socket.accept() #与客户端建立连接
    print('客户端地址:', addr)

    while True:
        cmd = conn.recv(BUFFERSIZE).decode('utf-8') #接收客户端输入
        print('cmd:', cmd)
        if len(cmd)<1 or cmd == 'quit': break
        
        res = subprocess.Popen(cmd, shell=True, 
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE) #执行客户端输入命令
        #以下标准输出信息都只能读取一次
        std_out = res.stdout.read() #获取输出到标准输出设备的成功信息
        std_err = res.stderr.read() #获取输出到标准输出设备的错误信息
        print("stdout:",std_out.decode('gbk'))
        print("stderr:",std_err.decode('gbk'))

        conn.send(std_out)
        conn.send(std_err)
    conn.close() #关闭连接

tcp_server_socket.close() #关闭socket
tcp-server-package
#!/usr/bin/env Python
# -*- coding: utf-8 -*-
#tcp_client_cmd.py

import socket

ip_port = ('127.0.0.1', 8080)  #服务端地址及端口
BUFFERSIZE = 1024   #设置缓冲区大小
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接

while True:
    cmd = input("Please input cmd<<< ").strip() #输入命令
    if len(cmd) < 1: 
        continue     #跳过本次循环,开始下一次循环
    elif cmd == 'quit': 
        tcp_client_socket.send(cmd.encode('utf-8')) #发送中断请求给服务端
        break     #中断循环

    tcp_client_socket.send(cmd.encode('utf-8'))
    ret = tcp_client_socket.recv(BUFFERSIZE)
    print(ret.decode('gbk'))

tcp_client_socket.close()
tcp-client-package

基于UDP协议实现(无黏包现象)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# udp_server_cmd.py

import socket
import subprocess

ip_port    = ('127.0.0.1', 8080)
BUFFERSIZE = 2048

udp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #设置为通过UDP协议通信
udp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
udp_server_socket.bind(ip_port)

while True:
    cmd, addr = udp_server_socket.recvfrom(BUFFERSIZE)
    print('client ip:',addr)

    cmd = cmd.decode('utf-8')
    print('cmd:',cmd)
    if len(cmd)<1 or cmd == 'quit':break

    res = subprocess.Popen(cmd, shell=True, 
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)


    std_out = res.stdout.read()
    std_err = res.stderr.read()
    print('stdout:', std_out.decode('gbk'))
    print('stderr:', std_err.decode('gbk'))


    udp_server_socket.sendto(std_out, addr)
    udp_server_socket.sendto(std_err, addr)

udp_server_socket.close()
udp-server-package
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# udp_client_cmd.py

import socket

ip_port    = ('127.0.0.1', 8080)
BUFFERSIZE = 2048

udp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_client_socket.connect(ip_port)

while True:
    cmd = input("Please input cmd<<< ").strip()
    if len(cmd)<1: continue
    elif cmd == 'quit': 
        udp_client_socket.sendto(cmd.encode('utf-8'), ip_port)
        break

    udp_client_socket.sendto(cmd.encode('utf-8'), ip_port)
    ret, addr = udp_client_socket.recvfrom(BUFFERSIZE)

    print(ret.decode('gbk'))

udp_client_socket.close()
udp-client-cmd

2.黏包的成因(基于TCP协议传输)

  • tcp协议的拆包机制
  • tcp面向流的通信是无消息保护边界的
  • tcp的Nagle优化算法:若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据
  • 接收方和发送方的缓存机制

3.导致黏包的根本因素

  • 接收方不知道消息之间的界限,不知道一次性提取多少字节的数据

4.黏包的解决方法

由于导致黏包的根本原因是接收端不知道发送端将要传送的字节流的长度,故有如下两种解决方案

方案一:在发送消息前,将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.py

"""
实现客户端远程操作服务端命令
"""
import socket
import subprocess

ip_port    = ('127.0.0.1', 8080) #服务端地址及端口
BUFFERSIZE = 1024 #设置缓冲区大小

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后,重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接

flag = True

while flag:
    conn, addr = tcp_server_socket.accept() #与客户端建立连接
    print('client ip addr:', addr)

    while True:
        cmd = conn.recv(BUFFERSIZE).decode('utf-8') #接收客户端输入
        if len(cmd)<1 or cmd == 'quit': 
            flag = False #防止死循环,在多个客户端连接时,可以去掉
            break

        res = subprocess.Popen(cmd, shell=True, 
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE) #执行客户端输入命令
        #以下标准输出信息都只能读取一次
        std_err = res.stderr.read() #获取输出到标准输出设备的错误信息
        if std_err:  #判断返回信息的类型
            ret = std_err
        else:
            ret = res.stdout.read() #获取输出到标准输出设备的成功信息
        
        """
        以下是方案一的核心部分
        """
        conn.send(str(len(ret)).encode('utf-8')) #发送要发送信息的长度
        print("ret:",ret.decode('gbk'))

        data = conn.recv(BUFFERSIZE).decode('utf-8') #接收客户端准备确认信息
        if data == 'recv_ready': 
            conn.sendall(ret) #发送所有信息

    conn.close() #关闭连接

tcp_server_socket.close() #关闭socket
tcp_server_package
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#client_tcp_cmd.py

import socket

ip_port = ('127.0.0.1', 8080)  #服务端地址及端口
BUFFERSIZE = 1024   #设置缓冲区大小
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接

while True:
    cmd = input("Please input cmd<<< ").strip() #输入命令
    if len(cmd) < 1: 
        continue     #跳过本次循环,开始下一次循环
    elif cmd == 'quit': 
        tcp_client_socket.send(cmd.encode('utf-8')) #发送中断请求给服务端
        break     #中断循环

    tcp_client_socket.send(cmd.encode('utf-8')) #发送要执行的命令

    """
    以下是方案一的核心部分
    """
    info_len = tcp_client_socket.recv(BUFFERSIZE).decode('utf-8') #接收要接收的信息长度

    tcp_client_socket.send(b'recv_ready') #给服务端发送已经准备好接收信息

    data     = b''
    ret_size = 0
    while ret_size < int(info_len): #判断信息是否已接收完
        data += tcp_client_socket.recv(BUFFERSIZE) #接收指定大小的信息
        ret_size += len(data)  #将已经接收的信息长度累加

    print(data.decode('gbk'))

tcp_client_socket.close()      #关闭socket
tcp_client_package
存在的问题:
程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

方案二:针对方案一的问题,引入struct模块,struct模块可以将发送的数据长度转换成固定长度的字节

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# tcp_server_cmd.py

"""
实现客户端远程操作服务端命令
"""
import socket
import subprocess
import struct
import JSON

ip_port    = ('127.0.0.1', 8080) #服务端地址及端口
BUFFERSIZE = 1024 #设置缓冲区大小

tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #设置为通过TCP协议通信(默认)
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#用于socket关闭后,重用socket
tcp_server_socket.bind(ip_port) #绑定ip和端口
tcp_server_socket.listen() #开始监听客户端连接

flag = True

while flag:
    conn, addr = tcp_server_socket.accept() #与客户端建立连接
    print('client ip addr:', addr)

    while True:
        cmd = conn.recv(BUFFERSIZE).decode('utf-8') #接收客户端输入
        if len(cmd)<1 or cmd == 'quit': 
            flag = False #防止死循环,在多个客户端连接时,可以去掉
            break

        res = subprocess.Popen(cmd, shell=True, 
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE) #执行客户端输入命令
        #以下标准输出信息都只能读取一次
        std_err = res.stderr.read() #获取输出到标准输出设备的错误信息
        if std_err:  #判断返回信息的类型
            back_info = std_err
        else:
            back_info = res.stdout.read() #获取输出到标准输出设备的成功信息
        
        """
        以下是方案二的核心部分(定制化报头)
        """
        head        = {'data_size':len(back_info)}
        head_json   = json.dumps(head) #将python对象转化为json字符串
        head_bytes  = bytes(head_json, encoding='utf-8') #将json字符串转化为bytes字节码对象
        head_struct_len = struct.pack('i', len(head_bytes)) #使用struct将定制化的报头打包为4个字节的长度
        conn.send(head_struct_len)  #发送定制报头的长度,4个字节
        conn.send(head_bytes) #发送定制报头信息

        print("back_info:",back_info.decode('gbk'))
        conn.sendall(back_info) #发送所有的真实信息

    conn.close() #关闭连接

tcp_server_socket.close() #关闭socket
tcp_server_package
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#client_tcp_cmd.py

import socket
import struct
import json

ip_port = ('127.0.0.1', 8080)  #服务端地址及端口
BUFFERSIZE = 1024   #设置缓冲区大小
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获取socket对象
tcp_client_socket.connect(ip_port) #与服务端建立连接

while True:
    cmd = input("Please input cmd<<< ").strip() #输入命令
    if len(cmd) < 1: 
        continue     #跳过本次循环,开始下一次循环
    elif cmd == 'quit': 
        tcp_client_socket.send(cmd.encode('utf-8')) #发送中断请求给服务端
        break     #中断循环

    tcp_client_socket.send(cmd.encode('utf-8')) #发送要执行的命令

    """
    以下是方案二的核心部分(定制化报头)
    """
    head_struct = tcp_client_socket.recv(4) #接收4字节的定制报头
    head_json_len = struct.unpack('i', head_struct)[0] #struct解包定制报头后是一个tuple,如(1024,)
    head_json = tcp_client_socket.recv(head_json_len).decode('utf-8') #将接收的bytes字节码报头解码为json字符串
    head = json.loads(head_json) #将json字符串转化为python对象
    print('head:',head)


    data     = b''
    ret_size = 0
    while ret_size < head['data_size']: #判断信息是否已接收完
        data += tcp_client_socket.recv(BUFFERSIZE) #接收指定缓冲大小的信息
        ret_size += len(data)  #将已经接收的信息长度累加

    print(data.decode('gbk'))  #windows默认编码是gbk

tcp_client_socket.close()      #关闭socket
tcp_client_package

5.TCP和UDP协议的简介

 待补充。。。

6.补充

1.[WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试

原因:端口被占用导致

解决:

Windows下
C:\> netstat -ano|findstr 8080             #查找8080端口占用进程号
TCP    127.0.0.1:8080         0.0.0.0:0              LISTENING       17496
C:\> tasklist |findstr 17496               #查找17496进程号对应的程序
python.exe                   17496 Console                    1     10,664 K
C:\> taskkill /pid 17496 /F                #杀掉17496进程
成功: 已终止 PID 为 17496 的进程。

Linux下
[root@localhost]# netstat -nltup | grep 80 #查找80端口上的程序
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      1479/Nginx  
[root@localhost]# ps -ef | grep nginx      #查找nginx对应进程号
root      1479     1  0 Jul23 ?        00:00:00 nginx: master process ./nginx
[root@localhost]# kill -9  1479            #杀掉1479进程

 2.struct模块可打包和解包的数据类型

3.socket模块方法说明

服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据
s.sendall()         发送TCP数据
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字

面向的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操作的超时时间
s.gettimeout()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件

socket模块方法

 

--结束END--

本文标题: Python Socket通信黏包问题分

本文链接: https://lsjlt.com/news/180366.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python Socket通信黏包问题分
    参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在TCP协议通信的情况下,才会产生黏包问题 基于TCP协...
    99+
    2023-01-30
    通信 Python Socket
  • Python数据传输黏包问题
    目录1.socket黏包问题原理2.UDP协议3.TCP协议4.发送方出现的黏包5. 接收方出现的黏包6.黏包的成因1.socket黏包问题原理 黏包:指数据与数据之间没有明确的分界...
    99+
    2024-04-02
  • Python数据传输黏包问题怎么解决
    本篇内容主要讲解“Python数据传输黏包问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python数据传输黏包问题怎么解决”吧!1.socket黏包问题原理黏包:指数据与数据之间没...
    99+
    2023-06-30
  • python socket粘包问题怎么解决
    今天小编给大家分享一下python socket粘包问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我...
    99+
    2024-04-02
  • python套接字socket通信
    目录一、初步认识二、socket对象一、初步认识 socket被翻译成套接字,尽管有些人诟病,但我觉得还挺贴切的。其功能是提供低级别的网络服务,最常用的就是根据IP来传输数据。 所谓...
    99+
    2024-04-02
  • Socket通信的示例分析
    这篇文章将为大家详细讲解有关Socket通信的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。 建立网络通信...
    99+
    2023-06-27
  • Java Socket通信如何摆平自身端口问题
    Java Socket通信如何摆平自身端口问题,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Java Socket通信在使用的时候有不少的问题,在端口编程上来说...
    99+
    2023-06-17
  • 在Java中使用Socket通信会遇到哪些问题
    本篇文章给大家分享的是有关在Java中使用Socket通信会遇到哪些问题,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Socket通信模型如图所示:不管Socket通信的功能有...
    99+
    2023-05-31
    java socket ava
  • 如何解决Socket粘包问题
    本篇内容介绍了“如何解决Socket粘包问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题一:TCP存...
    99+
    2024-04-02
  • Python基础之Socket通信原理
    上图是socket网络编程的流程图 至于数据在网络中是怎么走的,咱先不说,那个太底层了,咱今天见就说如何将数据从咱的屏幕上放到网络流中去。 这可不是键盘敲敲,回车一按的事情,在这背...
    99+
    2024-04-02
  • python如何实现socket简单通信
    这篇文章将为大家详细讲解有关python如何实现socket简单通信,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。首先先来简单介绍下socket:(具体更详细介绍的可以在网上找找,都讲得非常详细),这里主...
    99+
    2023-06-14
  • python套接字socket通信在实现
    本文小编为大家详细介绍“python套接字socket通信在实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“python套接字socket通信在实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、初步认识s...
    99+
    2023-06-29
  • 如何分析Socket TIME_WAIT 问题
    本篇文章给大家分享的是有关如何分析Socket TIME_WAIT 问题,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Socket TIME_WAIT 问题tcp/ip详解的卷...
    99+
    2023-06-04
  • Python与Java间Socket通信实例代码
    Python与Java间Socket通信   之前做过一款Java的通讯工具,有发消息发文件等基本功能.可大家也都知道Java写的界面无论是AWT或Swing,那简直不是人看的,对于我们这些开发人员还好,如...
    99+
    2022-06-04
    实例 代码 通信
  • python实现简单socket通信的方法
    本文实例讲述了python实现简单socket通信的方法。分享给大家供大家参考,具体如下: 刚刚开始接触python,实现了一个helloworld程序---关于udp协议的socket通信demo。 首先...
    99+
    2022-06-04
    通信 简单 方法
  • Python中Socket通信的原理是什么
    本篇文章给大家分享的是有关Python中Socket通信的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。python是什么意思Python是一种跨平台的、具有解释性、...
    99+
    2023-06-14
  • socket连接关闭问题分析
    socket编程过程中往往会遇到这样那样的问题,出现了这些问题,有的是由于并发访问量太大造成的,有些却是由于代码中编程不慎造成的。比如说,最常见的错误就是程序中报打开的文件数过多这个...
    99+
    2024-04-02
  • 分布式系统中的 HTTP 通信问题:面试必问问题
    随着分布式系统的普及,HTTP 通信成为了系统之间常用的通信方式之一。然而,在实际应用中,HTTP 通信也会遇到一些问题。本文将从面试必问问题的角度,介绍分布式系统中的 HTTP 通信问题,并提供相应的解决方案。 HTTP 请求的幂等性...
    99+
    2023-06-16
    http 面试 分布式
  • 如何解决Java Socket通信技术收发线程互斥的问题
    本篇内容介绍了“如何解决Java Socket通信技术收发线程互斥的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Java Socket...
    99+
    2023-06-17
  • java中处理socket通信过程中粘包的情况
    这两天学习了java中处理socket通信过程中粘包的情况,而且很重要,所以,今天添加一点小笔记。处理粘包程序是客户端的接受消息线程:客户端:import java.io.InputStream; import java.io.InputS...
    99+
    2023-05-31
    java socket 粘包
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作