当前位置: 首页 > 知识库问答 >
问题:

两个线程能否同时与同一端口上的两个不同套接字交互?

杨征
2023-03-14

假设有一个多线程服务器将数据写入同一端口上的两个不同套接字,其中一个专用线程处理每个套接字。两个线程是否可以同时写入各自的套接字?(所谓“同时”,我指的是真正的同时性,而不仅仅是并发交错。)或者,套接字共享同一端口的事实是否意味着强制执行互斥?

一般来说,我不清楚如何在两个任意I/O流之间共享资源。我知道两个线程不能同时写入磁盘,因为磁盘本身是共享资源。然而,在套接字和端口的情况下,我没有类似的物理模型来指导我的推理。一个相关的问题是不同类型的I/O流之间是否有共享资源-例如,两个线程之间是否会有任何争用写入两个文件描述符,一个用于网络套接字,另一个用于磁盘上的文件?

共有1个答案

公羊嘉
2023-03-14

虽然相互排斥是强制的,但这并没有太大的帮助,可能会导致交叉书写。。。让我解释一下。

从linux手册阅读(3):

I/O对于普通文件、管道和FIFO来说是原子的。原子意味着一个操作中的所有字节一起开始,然后一起结束,而不会与其他I/O操作交错。这是终端的一个已知属性,它不受尊重,并且终端被显式(和隐式永久)排除,使得行为未指定。其他设备类型的行为也未指定,但该措辞意在暗示未来的标准可能会选择指定原子性(或不指定原子性)。

套接字算作先进先出。

此外,套接字共享相同的底层硬件,因此同一以太网接口上的所有套接字都必须同步才能通过实际网络(即使在不同的套接字之间也会发生同步)。

因此,从技术上讲,您可以从两个或多个线程(甚至进程)写入,而不会重创IO层并造成一大堆混乱。

但是,您应该注意到,并不是所有的字节都保证写入fd。正如《写作人》中提到的:

write()从缓冲区指向的buf向文件描述符fd引用的文件写入多达个字节。

当两个(或更多)进程/线程尝试写入同一套接字时,这可能会导致碎片。

(编辑-参见注释)此外,对write的大型调用可以在内部分割为多个write调用(根据fd的内部缓冲区)。这可能导致内部文件锁被释放和重新获取,从而导致交叉写入数据。

例如,假设以下场景:

Thread1 calls `write(fd, "Hello long message", 18);`

Context switch.

Thread2 calls `write(fd, "Hello another message", 20);`

Context switch.

Thread1 gets the return value of 7 (there wasn't enough room in the buffer for the long message), meaning only some off the message was sent.

Context switch (system does some stuff too).

Thread1 gets the return value of 10 (there wasn't enough room in the buffer for the long message), meaning only some off the message was sent.

在这个场景中,客户端得到了类似于“Hello lHello anot”(或者可能是“Hello Anothlo l”,因为在这种情况下,写入操作的顺序可能无法保证),这不是预期的效果。。。

因此,即使操作系统保证都可以被视为“原子的”(我们没有得到“Hellolo…” ),您仍然会发现自己需要一个用户陆地缓冲区和某种同步器来管理并行写入。

如果您感到懒惰,我为我的项目编写了一个小的两文件库,用于常见的多线程sock操作。我认为我还没有对它进行过两次并行线程写入的测试,但一旦我实现了HTTP/2,我就会进行测试。

这个库并不是我所能想到的最面向性能的代码,但它可能会为您提供一个起点。

P. S.

从您的问题来看,似乎您正在编写一个服务器应用程序,每个客户端使用一个线程。。。如果是这样,我敦促你重新考虑。

当DoS攻击导致产生足够多的线程时,运行此类服务器应用程序的任何机器的重载都会导致整个机器崩溃。机器陷入了执行更多上下文切换而不是任务的困境,直到它只是上下文切换而什么都不做(可能除了烧掉CPU)。

基本上,在底层实现中,每个fd都被分配了一个“锁”(我在简化,但基本上是这样做的)。

读取写入和任何被认为是原子的I/O操作将在执行任何操作之前尝试获取锁。(编辑:)但是,在内部,当刷新内部fd的缓冲区时,可以释放并重新获取锁。

这意味着这些操作仅对相同的fd是原子操作。因此,影响相同的fd的任何两个I/O操作都将彼此同步,并给人以原子的印象。

在较低的级别上,由于套接字共享相同的硬件(假设您有一个网络接口),tcp数据包在发送时是同步的,因此不会出现tcp数据包碎片。

然而,这是一个与I/O C API不同的同步问题,因为操作系统提供的I/O(C API)写入到中间内部缓冲区。(EDIT):当缓冲区已满时,内部fd锁可能会在缓冲区刷新时释放,从而导致可能的交错数据。

内核在写入文件、套接字、管道等时管理这个缓冲区,每个缓冲区都有自己独特的同步问题...即非SSD硬盘需要与磁盘旋转同步写入,不能真正提供良好的硬件并发

内核问题、硬件问题和API约束都是会丢失或实现并发的不同级别。某些失去并发性的操作通过硬件加速获得性能。。。

... 最后,作为软件开发人员,我们尽了最大努力,希望内核和硬件尽最大努力。

 类似资料:
  • 我有大约60个套接字和20个线程,我想确保每个线程每次都在不同的套接字上工作,所以我根本不想在两个线程之间共享同一个套接字。 在我的类中,我有一个后台线程,它每60秒运行一次并调用方法。在方法中,我迭代我拥有的所有套接字,然后通过调用类的方法开始逐个ping它们,并根据响应将它们标记为活的或死的。在方法中,我总是需要迭代所有套接字并ping它们以检查它们是活的还是死的。 现在,所有读取器线程将并发

  • > 当同一主机上的两个进程使用网络套接字通信时,它们的套接字必须使用不同的端口吗? 我想是的,因为套接字由IP地址和端口号标识。如果进程的套接字使用相同的端口,则无法区分它们的套接字,对吗? 但是一个主机可以有多个网络接口,所以有多个IP地址。同一个主机上的多个网络接口是否可以共享一个端口,从而使两个套接字共享同一个端口? 例如,SSH本地端口转发由以下选项指定: 然后本地主机(SSH客户端运行的

  • 这可能是一个很基本的问题,但它使我感到困惑。 两个不同连接的套接字可以共享一个端口吗?我正在编写一个应用服务器,它应该能够处理100K以上的并发连接,我们知道一个系统上可用的端口数量大约是60K(16bit)。一个连接的套接字被分配给一个新的(专用的)端口,因此这意味着并发连接的数量受到端口数量的限制,除非多个套接字可以共享同一个端口。所以问题是。

  • 是否有一种简单的(非迭代的)方法来绑定两个套接字,一个TCP和一个UDP,以保证它们都具有相同的临时端口? 我有一个简单的服务器,最初通过TCP连接,但也通过UDP传输。我不想在连接后从客户端发送UDP数据包,只是为了让服务器发现客户端的UDP临时端口。除非必要,否则我不想通过初始传输控制协议消息发送端口号。 现在,我在TCP套接字上的getsockname之后显式绑定客户端UDP套接字的端口,以

  • 两个线程可以同时使用同一个套接字吗? 例如,首先我打开一个表示Android客户端和windows 7服务器之间连接的套接字。我想使用DataOutpuStream发送一个文件大小的整数,然后在此之后我将使用BufferedOutpuStream发送二进制文件,例如pdf 服务器将获取客户端发送的大小,然后检查收到的文件大小是否正确。它将发送整数、文件大小编号与收到的文件的实际大小进行比较。如果有