【问题标题】:Data transfer over TCP-IPv6 connection通过 TCP-IPv6 连接进行数据传输
【发布时间】:2012-01-27 18:47:26
【问题描述】:

我正在使用 C 语言和 Linux 平台开发客户端-服务器应用程序。我想要实现的是在客户端和服务器上通过 TCP 连接更改套接字 ID,而不会丢失数据,其中客户端将数据从文件发送到主线程中的服务器。该应用程序是多线程的,其他线程根据设置的一些全局标志更改套接字 ID。

问题:应用程序通过 IPv4 和 IPv6 路径建立了两个 TCP 套接字连接。我首先在主线程中通过 TCP-IPv4 连接传输文件。另一个线程正在检查一些全局标志,并且可以访问/共享为主线程中的每个协议创建的套接字 ID。 send 和 recv 在其调用中使用指针变量来指向要用于数据传输的套接字 ID。数据最初通过 TCP-Ipv4 传输。一旦设置了全局标志并且进行了很少的其他检查,其他线程将更改用于发送调用的套接字 ID 以指向 IPv6 套接字。该线程还负责在两个主机之间传达更改。在切换之前,我将通过 IPv4 完全发送所有数据。此外,在刚刚切换套接字 ID 后,我正在通过 Ipv6 发送数据。但是在传输过程中,通过 IPv6 连接会丢失数据。(我在服务器端 send(*p_dataSocket.socket_id,sentence,p_size,0); 的发送函数中使用指针变量来动态更改指向 IPv6 套接字 ID 的指针)

两边recv和send调用后的错误分别是ESPIPE:Illegal seek,但是这个错误在切换之前就已经存在了。所以我很确定这与数据丢失无关

我正在使用pselect() 检查每个套接字的可用数据。我可以以某种方式理解切换时的数据丢失(如果处理不当),但我无法弄清楚为什么在切换后的传输过程中会发生数据丢失。我希望我清楚问题是什么。我还检查了通过每个协议单独发送数据而不进行切换,并且没有数据丢失。我最初通过 IPv6 传输数据,然后切换到 IPv4,没有数据丢失。除了使用errno 或netstat 之外,还希望知道如何调查此问题。

【问题讨论】:

  • 我怀疑你需要做更多的解释。您是在处理客户端还是服务器或两者都处理?当然,一旦在套接字上建立了连接,它要么是 IPv4 要么是 IPv6 连接;如果不建立新的套接字连接,您将无法切换到其他协议,对吗?
  • @user369823,让我看看我是否明白,你在客户端和服务器之间同时建立两个 TCP套接字(tcp和tcp6),然后开始数据传输通过tcp 套接字。在中间的某个地方,您尝试通过tcp6 套接字完成上述数据传输?这样做有什么可取的?如果您对切换套接字是认真的,为什么不在准备切换时关闭tcp 套接字?
  • @Mike,感谢您的回复-是的,您是对的。我在主线程中已经建立了两个连接。另一个线程基于一些标志和条件将发送函数中使用的指针切换到 IPv6 套接字。然后通过 IPv6 连接传输数据。我不确定关闭以前的连接会有什么帮助。当我通过 Ipv6 发送数据然后切换到 Ipv4 时,没有数据丢失。我不知道为什么反之亦然时会丢失数据。

标签: c tcp network-programming client-server ipv6


【解决方案1】:

当您使用 TCP 发送数据时,您不能丢失之间的部分信息。您要么以发送的方式接收字节流,要么根本不接收任何内容——前提是您正确使用了与套接字相关的功能。

您可能需要调查几点。

  • 首先,您必须确保您确实发送了丢失的数据。在服务器端应用程序上添加一些日志记录:将您通过 send() 传输的任何内容转储到某个文件中。还包括一些额外的信息,例如:

    数据包号==1234,*p_dataSocket.socket_id==11,Data=="data_contents_here",共22字节;发送()返回==22

这里重要的是看*p_dataSocket.socket_id的内容。确保您使用的是互斥锁或类似的东西,因为您有一个定期读取socket_id 内容的线程和另一个偶尔更改它的线程。除非您的线程在读/写时拥有对它的垄断访问权,否则您不能保证不会从该地址获取错误值。这对于正常的程序操作和调试信息的生成都很重要。

这里另一个可能的问题是选择sentence 发送的逻辑。在多线程程序中可能很难跟踪此变量的损坏。传输信息的记录也会在这里为您提供帮助。

  • 使用任何 TCP 嗅探器来检查 TCP 堆栈真正传输的内容。是否有丢失数据的数据包?如果没有这些数据包,请尝试找出哪个 send() 调用负责发送该数据。如果这些数据包存在,请检查接收方是否存在错误。

  • errno 值不应单独使用。只有当你从函数中得到错误的返回值时,它的值才有意义。尝试找出 errno 何时变为 ESPIPE 当任何 API 函数返回类似 -1 的内容时可能会发生这种情况(取决于函数)。当您发现它发生在哪里时,您应该找出那段特定代码中的错误(调试器是您的朋友)。请记住,多线程环境中的errno 行为取决于您的系统实现。确保使用 -pthread 选项 (gcc) 或至少使用 -D_REENTRANT 进行编译以将风险降至最低。

  • 检查 this question 以获取有关 errno==ESPIPE 情况的可能原因的一些信息。按照那里的建议,尝试一些调试技术。 ESPIPE 的 Errno 值提示您在程序中的某处错误地使用了文件描述符。也许您在某处使用套接字 fd 作为常规文件或类似的东西。这可能是由某种竞争条件(从多个线程同时访问一个对象)引起的。

【讨论】:

  • @Pavel-感谢所有建议。我正在记录发送的数据和发送的套接字 ID,用于每个发送调用。但我也会记录每次 send() 调用后发送的数据量。我也在使用互斥锁,同时仅在另一个线程中更改套接字 ID。我想知道这是否足够?
  • 我也在编译时使用了 pthread 选项。对于 ESPIPE 错误,我不确定如何检查竞态条件,但会熟悉 GDB。该程序在读取和写入数据时运行良好。我还在我的可执行文件上做了 strace,它在 lseek 上显示了 ESPIPE,这是在打开文件时内部调用的。我无法找到其中可能出现的问题。
  • @user369823 不,仅在一个线程中使用互斥锁是不够的。事实上,这与根本不使用互斥锁是一样的。请记住,虽然正确使用同步原语非常重要,但同步产生的错误通常是不稳定的。我的意思是,如果省略互斥锁是问题的原因,那么有时您会收到错误,而不是每次切换时都会出错。不管怎样,试着记录一些东西,看看会发生什么。
  • 再次感谢回复。我刚刚检查了 ESPIPE 错误。事实证明,正如您所说,如果没有为任何调用返回错误并设置为未定义/不相关的内容,则不应使用 perror。我改为使用 strerror_r() 检查它并返回成功。
  • 其次,我看到接收或发送数据时没有返回错误。似乎是路由器丢弃数据包的问题,​​因为当我通过 IPv6 传输数据很短时间然后切换到 IPv4 时,没有数据丢失,反之亦然。我还尝试通过每个协议单独发送数据。 Ipv4 没有数据丢失,但对于 Ipv6,我有时会获得全部数据,有时却没有。需要进行更多调试
猜你喜欢
  • 2020-06-13
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 2017-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多