Python Socket 接收/发送多线程

2024-01-06

我正在编写一个Python程序,在主线程中,我使用recv函数连续(循环)通过TCP套接字接收数据。在回调函数中,我使用 sendall 函数通过同一套接字发送数据。什么触发回调是无关紧要的。我已将套接字设置为阻塞。

我的问题是,这样做安全吗?我的理解是回调函数是在单独的线程(而不是主线程)上调用的。 Python套接字对象是线程安全的吗?根据我的研究,我得到了相互矛盾的答案。


Python 中的套接字不是线程安全的。

您试图同时解决几个问题:

  1. 套接字不是线程安全的。
  2. recv 是阻塞的并阻塞主线程。
  3. sendall 正在从不同的线程使用。

您可以使用以下方法解决这些问题asyncio https://docs.python.org/3/library/asyncio.html或者以 asyncio 内部解决的方式解决它:通过使用select.select与一个socketpair,并使用队列来接收传入数据。

import select
import socket
import queue

# Any data received by this queue will be sent
send_queue = queue.Queue()

# Any data sent to ssock shows up on rsock
rsock, ssock = socket.socketpair()

main_socket = socket.socket()

# Create the connection with main_socket, fill this up with your code

# Your callback thread
def different_thread():
    # Put the data to send inside the queue
    send_queue.put(data)

    # Trigger the main thread by sending data to ssock which goes to rsock
    ssock.send(b"\x00")

# Run the callback thread

while True:
    # When either main_socket has data or rsock has data, select.select will return
    rlist, _, _ = select.select([main_socket, rsock], [], [])
    for ready_socket in rlist:
        if ready_socket is main_socket:
            data = main_socket.recv(1024)
            # Do stuff with data, fill this up with your code
        else:
            # Ready_socket is rsock
            rsock.recv(1)  # Dump the ready mark
            # Send the data.
            main_socket.sendall(send_queue.get())

我们在这里使用多个构造。您必须用您选择的代码填充空白区域。至于解释:

我们首先创建一个send_queue这是要发送的数据队列。然后,我们创建一对连接的套接字(socketpair())。稍后我们需要这个来唤醒主线程,因为我们不希望这样做recv()阻止并阻止写入套接字。

然后,我们连接main_socket并启动回调线程。现在神奇之处在于:

在主线程中,我们使用select.select知道是否rsock or main_socket有任何数据。如果其中之一有数据,则主线程被唤醒。

将数据添加到队列后,我们通过信号唤醒主线程ssock哪个醒来rsock从而返回select.select.

为了完全理解这一点,您必须阅读select.select() https://docs.python.org/3/library/select.html#select.select, socketpair() https://docs.python.org/3/library/socket.html#socket.socketpair and queue.Queue() https://docs.python.org/3/library/queue.html#queue.Queue.


@tobias.mcnulty 在评论中提出了一个很好的问题:为什么我们应该使用Queue而不是通过套接字发送所有数据?

您可以使用socketpair也发送数据,这有其好处,但由于多种原因,通过队列发送可能更可取:

  1. 通过套接字发送数据是一项昂贵的操作。它需要系统调用,需要在系统缓冲区内来回传递数据,并且需要充分利用 TCP 堆栈。用一个Queue保证我们只有 1 次调用 - 对于单字节信号 - 而不是更多(除了队列的内部锁,但那是相当便宜的)。通过发送大数据socketpair将导致多个系统调用。作为提示,您也可以使用collections.deque由于 GIL,CPython 保证了线程安全。这样你就不需要任何系统调用了socketpair.
  2. 从架构角度来看,使用队列可以让您稍后进行更细粒度的控制。例如,数据可以以您希望的任何类型发送,然后进行解码。这使得主循环变得更加智能,并且可以帮助您创建更简单的界面。
  3. 你没有尺寸限制。它可能是一个错误或一个功能。我相信改变系统的缓冲区大小并不完全被鼓励,这对您可以发送的数据量产生了自然的限制。这可能是一个好处,但应用程序可能希望自己控制它。使用“自然”功能将导致调用线程挂起。
  4. 就像socketpair.recv系统调用,对于大数据,您将通过多个select也可以打电话。 TCP 没有消息边界。您要么必须创建人工套接字,将套接字设置为非阻塞并处理异步套接字,要么将其视为流并连续通过select根据您的操作系统,调用可能会很昂贵。
  5. 支持同一套接字对上的多个线程。从多个线程通过套接字发送 1 个字节用于发信号就可以了,这正是 asyncio 的工作原理。发送超过此数量可能会导致数据以错误的顺序发送。

总而言之,在内核和用户空间之间来回传输数据是可能的并且可行,但我个人不推荐这样做。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python Socket 接收/发送多线程 的相关文章

随机推荐

  • Scala 2.8 CanBuildFrom

    继我问的另一个问题之后 Scala 2 8 突破 https stackoverflow com questions 1715681 scala 2 8 breakout 我想更多地了解一下 Scala 方法TraversableLike
  • C 的 INI 文件解析器

    我需要在 C 中读 写 INI 文件 对我来说有点奇怪的是我找不到任何标准的方法来做到这一点 在 C 中处理 INI 文件的常用方法是什么 我更喜欢平台无关的和 Linux INI 文件解析器库 This link http blog br
  • 删除 SwiperJS 中用数组映射的幻灯片而不重置状态

    我已经为这个问题苦苦挣扎了很长一段时间 但一直无法弄清楚 在这里编写沙箱演示代码 https codesandbox io s swiper remove slides w4gy3 file src App js 我有一组渲染的幻灯片Swi
  • 浅克隆中的“嫁接”提交到底是什么?

    在 git 中进行浅克隆时 使用 depth选项 根提交被标记为grafted 谷歌搜索没有找到任何令人满意的文档 好像没有什么关系git 移植 https git wiki kernel org index php GraftPoint
  • jQuery 数据表行顺序

    dTable periods grid dataTable bProcessing false bPaginate false bDestroy true bFilter false aaData myArray 为什么行顺序混乱 我希望行
  • Google Docs API:文档资源 ID 是否会改变?

    构建一个类似网站的环境 其中所有链接都基于文档中的资源 ID 是否明智 文档 ID 过去是否发生过变化 将来是否会发生变化 上周 我们惊讶地发现一个集合中的某些文档 ID 似乎已更改 这可能吗 id 是条目的唯一 永久标识符 因此它永远不应
  • 使用代理时 Nginx 502 Bad Gateway 错误

    我有一个 Angular 构建和一个 Laravel 后端 提供在一台服务器上运行的 API 我已经在 nginx 中配置了它们 前端有一个到后端服务器的代理 后端在 url 上运行 示例是占位符 http api example com
  • 在react-router v4中使用React IndexRoute

    我正在通过在线教程自己学习 React 这是一个关于使用 React Router 的基本示例
  • SplFileObject 错误无法打开流:没有这样的文件或目录

    我正在尝试在 symfony2 中实现存折网络服务并遵循此存折捆绑 https github com eymengunay PassbookBundle我的控制器看起来像这样 if form gt isValid Create an eve
  • 审计跟踪和实施 SOX/HIPAA/等敏感数据的最佳实践

    我认为自己在应用程序设计方面相对熟练 但我从未接触过敏感数据 我一直想知道审计跟踪的最佳实践是什么以及应该如何实施它们 我现在不必这样做 但如果医疗公司要求我为他们做一些工作 能够自信地与他们交谈 那就太好了 假设我们有一个 学校 数据库
  • 在spring配置文件中设置资源

    我正在尝试在弹簧配置中配置推土机 当使用 xml 配置时 它会像
  • MPI 发送数组的数组

    好的 所以我尝试通过 MPI 发送这样的结构 struct BColumns double B int offset 如果我只是像这样对数据进行一些BS分配 bSet offset myRank bSet B double calloc 2
  • django 基于类的视图 - UpdateView - 如何在处理表单时访问请求用户?

    在 Django 的基于类的 UpdateView 中 我排除了用户字段 因为它是系统内部的 我不会要求它 现在将用户传递到表单的正确 Django 方法是什么 我现在的做法是将用户传递到init表单的 然后重写表单的 save 方法 但我
  • 更改 setup.py 中的输出目录

    我正在使用 setuptools 中的设置来创建setup py 我想知道是否可以以编程方式更改输出目录以将其更改为dist 我知道您可以使用以下命令从命令行执行此操作 dist dir标志 但我希望能够做到within the setup
  • 非公开 API 使用:应用程序包含一个或多个损坏的二进制文件

    我对 iOS 应用程序新版本上传的情况感到非常沮丧 这是故事 日期2018 年 1 月 25 日我们已经上传了新版本3 3 27 内部版本号 1 0 该过程已成功完成并可用于试飞 之后我们发现了一些问题并继续2018 年 1 月 26 日我
  • 如何根据屏幕密度(DPI大小)选择资源?

    我正在尝试根据我的颤振应用程序中的显示密度加载图标 如何根据屏幕密度动态加载 hdpi xhdpi xxhdpi 经过几个小时的搜索 根据官方文档 https flutter dev docs development ui assets a
  • 如何解决“错误的解释器:没有这样的文件或目录”

    我正在尝试运行sh脚本并在 Mac 上收到以下错误 usr bin perl M bad interpreter No such file or directory 我怎样才能解决这个问题 Remove M控制字符 perl i pe y
  • 根据 Angular 反应形式中选择的数字推送迭代行

    当我在下拉列表中选择一个数字时 我在迭代行时遇到问题 我将如何推动该数字 以便根据该数字迭代行 卡 我的流程是这样的 首先您必须将 Solo Traveler 选择为 否 以便出现成人和儿童下拉菜单 该下拉列表是我的问题 我将如何根据所选的
  • 使用 dplyr 与数据库中的日期字段进行数据库连接

    有什么魔力可以使用吗dplyr当数据库有日期字段时访问数据库 A dplyr tbl df将 mysql 日期时间字段转换为 chr 如果我能的话那也不错as Date 他们回来了 但如果我之前这样做collect ing 该表 我收到一个
  • Python Socket 接收/发送多线程

    我正在编写一个Python程序 在主线程中 我使用recv函数连续 循环 通过TCP套接字接收数据 在回调函数中 我使用 sendall 函数通过同一套接字发送数据 什么触发回调是无关紧要的 我已将套接字设置为阻塞 我的问题是 这样做安全吗