0

我想通过 TCP 连接两个程序。我的主程序是用 Qt 编写的,需要与另一个用 Python 编写的程序通信。我考虑使用 TCP 套接字和 Google 的 protobuf 来交换消息。在 Qt 中,我使用一个 QTcpSocket 来接受连接并从流中读取,只要它的 readyRead-Signal 被触发。在 python 中,我还使用 tcp-socket 并发送消息。

这很有效,只要没有一方被杀。目前,python 端正在向 C++ 端发送消息。( socket.send(str(id)+"\ņ")) 每次发送后,我都会检查异常情况(对等方重置连接、管道损坏……)以查看是否收到了消息。如果我杀死 C++ 程序,从 python 客户端发送的下一条消息不会触发异常,但显然没有收到。下一条消息触发异常,但最后一条消息丢失。

经过一番试验,我发现socket.send("\n")在每条消息之后发送一条空消息( )可以解决问题。我现在做的

try:
  s.send(str(id)+"\n");
  s.send("\n")
  sleep(0.5)
except socket.error,v: 
   print "FAILed to send",id,v[0],v[1]

并在 C++-Peer 被杀死后立即收到异常(s.send(str(id)+"\n\n")但调用无济于事)。

最后,我的问题是:这是检查我的消息是否收到的可靠方法吗?我不想切换到 UDP,因为我不想为每条消息实现自己的 ACK 消息。

这是我第一次在 python 和 C++ 中使用套接字,并且无法真正解释为什么我的方法有效,所以我使用它有点不舒服。

有人可以告诉我更多吗?send(int(id)+"\n")我猜python套接字在发送后首先需要一个ACK,send("\n")然后意识到管道已损坏。这个对吗?

4

1 回答 1

0

当远程对等方断开 TCP 连接时,您的 TCP 套接字将准备好读取,然后当您尝试从中进行 recv() 时,recv() 将返回 0。

当然,如果您的发送程序只调用 send() (您的 Python 程序的方式),那么它不会注意到套接字的接收端发生了什么,并且您最终会遇到您描述的问题。

另一方面,您也不想盲目地调用 recv(),因为如果调用了 recv() 并且远程对等方没有发送任何数据,则 recv() 将阻塞等待数据,除非远程对等方实际上发送了一些,您将陷入僵局。

处理这个问题的最简单方法是使用 select() 来多路复用 I/O,这样 Python 脚本就可以知道何时调用 send() 和/或 recv() 是合适的。像这样的东西:

import socket
import select

[...]
while 1:
   socketsToReadFrom = [s]
   if (you_still_have_more_data_to_send):
       socketsToWriteTo = [s]
   else:
       socketsToWriteTo = None

   # This select() call will block until there's something to do
   socketsReadForRead, socketsReadyForWrite, junk = select.select(socketsToReadFrom, socketsToWriteTo, None)

   if (s in socketsToReadFrom):
      readBytes = s.recv(1024)
      if (len(readBytes) > 0): 
         print "Read %i bytes from remote peer!" % readBytes
      else:
         print "Remote peer closed the TCP Connection!!"
         break

   if ((socketsToWriteTo != None) and (s in socketsToWriteTo)):
      s.send(some_more_data)

至于验证是否收到您的消息,这有点棘手,因为 TCP(和网络堆栈)进行了大量的流水线/缓冲。特别是,从 send() 成功返回只会告诉您您的数据已被传递到本地 TCP 堆栈的输出数据缓冲区;这并不意味着数据已经到达远程对等点。如果您真的想要远程对等方已经处理数据的“收据”,则必须让远程对等方发回某种确认。请注意,在 TCP 下,这种复杂程度通常是不必要的,因为除非网络或硬件故障(或远程对等方关闭 TCP 连接),您可以相当确定 TCP 堆栈最终会将您的数据获取到那里;例如,如果一个数据包被丢弃,TCP 堆栈将自动重新发送它。只有当网络连接长时间停止工作(例如几分钟)时,才会发生数据丢失,此时 TCP 堆栈将放弃并关闭 TCP 连接。

于 2013-08-15T14:59:34.587 回答