我想通过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 ++的套接字,并不能真正解释为什么我的方法有效,所以我使用它有点不舒服。
有人能告诉我一点吗?我想python套接字在发送send(int(id)+"\n")
之后需要第一个send("\n")
的ACK,然后意识到管道坏了。这是对的吗?
答案 0 :(得分:0)
当远程对等方断开TCP连接时,您的TCP套接字将准备好读取,然后当您尝试从其中recv()时,recv()将返回0.
当然如果你的发送程序只调用send()(你的Python程序的方式),那么它就不会注意到套接字的recv端发生了什么,你最终会遇到你描述的问题。
另一方面,你也不想盲目地调用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连接。