2014-10-30 46 views
0

我正试图在一个系统上工作,其中服务器将建立一个连接并实时向客户端发送多个数据。Twisted TCP可能在同一连接内发送两个独立的数据?

为了模拟“实时”,我的服务器将发送接连2数据的一个有1秒的延迟

class IphoneChat(Protocol): 
    def connectionMade(self): 
    print "a client connected" 
    self.transport.setTcpNoDelay(True) 
    msg = 'F#m' 
    b = msg.encode('utf-8') 
    msg2 = 'C' 
    c = msg2.encode('utf-8') 
    self.transport.write(b) 
    time.sleep(1) 
    self.transport.write(c) 

我的Android客户端将基本收到的每个数据,并立即打印出来彼此

while(i<2){ 
        Log.d("waiting", "waiting"); 
        bytesRead = inputStream.read(buffer); 
        byteArrayOutputStream.write(buffer, 0, bytesRead); 
        response = byteArrayOutputStream.toString("UTF-8"); 
        i++; 
        Log.d("response", response); 

        //immediately run UIthread 
        runOnUiThread(new Runnable() { 
         @Override 
         public void run() { 

          textResponse.setText(response); 
         } 
        }); 

       } 

但是,看起来客户端同时采用F#m和C,并将它们一起显示为“F#mC”而不是“F#m”,然后显示为“C”。

是因为TCP无法做我提到的,我不得不使用UDP来做到这一点?

+0

不要使用'time.sleep'在一个Twisted程序中。在Twisted程序中延迟动作的方法是'reactor.callLater'。 – 2014-11-08 13:00:34

回答

1

基本上,是的,那是因为TCP无法完成你所提到的。 TCP是一个字节流,而不是消息流。就TCP而言,您已发送字节F,#,mC,以及客户端是将它们读取为3个字节,后跟1个字节,一次4个字节,或2个字节后跟2个字节为完全是任意的。*

但这并不意味着你必须使用UDP。如果你想通过TCP发送单独的消息,你只需要想出一些方法来自己分离消息 - 长度前缀,终止符或分隔符,消息的自我描述格式等。

In扭曲,你通常在Protocol课上做这个;事实上,这几乎是协议的重点。

查看Sockets are byte streams, not message streams了解在TCP之上构建简单协议的一些常规(非扭曲)示例。但是,如果你的消息是纯文本而没有可能的新行,那么一个简单的(并且易于人工调试的)简单的方法就是在单独一行中发送每条消息,就像你写消息的方式一样一个文件:

b = (msg + '\n').encode('utf-8') 

的客户端将需要缓冲了它接收到的字节,存储它们,直到它看到一个或多个\n字符,然后分割并处理每个完整线作为消息,并记住最后不完全并从那里继续缓冲。 Python/Twisted有很多工具可以使这部分变得简单;我不知道Android。但你总是可以做手工,是这样的(未经测试准的Java伪代码):

因为这样的数据包和缓冲区等工作
ByteArray buf = new ByteArray(); 
while(i<2){ 
    Log.d("waiting", "waiting"); 
    bytesRead = inputStream.read(buffer); 
    buf.append(buffer, 0, bytesRead); 
    Array<ByteArray> lines = buf.split('\n'); 
    buf = lines.popLast(); 
    for (ByteArray line in lines) { 
     byteArrayOutputStream.write(line, 0, line.length()); 
     response = byteArrayOutputStream.toString("UTF-8"); 
     i++; 
     Log.d("response", response); 

     //immediately run UIthread 
     runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 

       textResponse.setText(response); 
      } 
     }); 
    } 
} 

*在实际应用中,有时你会看到读取准确匹配写入 - 特别是在本地主机上测试时,在未加载的机器上测试,写入之间有很长的间隔等。但是,在真实条件下,您不能依赖这些写入。

+0

Abarnert,谢谢你的建议。我可以澄清一下,你的代码似乎表明它需要读取和使用\ n从读缓冲区分离msg。但是,它是否解决了在感知示例中实时读取它的问题:'C \ n'发送,5秒钟后'D \ n'被发送。客户端是否等待C和D发送5秒,然后执行上述分离,或者一旦它接收到C \ n,它能够立即提取出来,同时等待下一个(D )?而后者就是我想实现的目标 – 2014-10-30 22:25:16

+0

@LimGangyi:客户端不会等待5秒钟。每当它至少得到'C \ n'' - 应该马上就会 - 它会处理'C'。但是,当然总是有可能的是,丢包和重发以及所有这些都会导致'C \ n''直到'D \ n'(或者D')才会到达已经到达。这只是保证它仍然会将“C”和“D”作为单独的消息进行处理。 – abarnert 2014-10-30 22:34:28

+0

@LimGangyi:如果你想要更多的实时行为,以更高的开销为代价,你可以关闭TCP_NODELAY套接字选项。请参阅[Wikipedia关于Nagle算法的文章](http://en.wikipedia.org/wiki/Nagle's_algorithm)来理解这一点。但是,除非你有基本的协议工作,否则不要这样做。 – abarnert 2014-10-30 22:37:12

0

我实际上已经根据@abarnert的建议修改了代码库。

我的客户端现在可以单独打印字符。然而,问题在于,不是实时打印出来,而是等到所有数据发送之后才按顺序打印结果,但是以ms为单位。

我的服务器实际上每隔5秒发送2个数据,例如: 一旦连接开始,等待5秒,发送第一个数据,等待5秒,发送第二个数据。 但是,在我的android客户端,它等待10secs,然后再打印出两个数据。

我在这里做错了什么,使它无法实时打印它们吗?

问候,

\\\\\\\\\\\\\\\\\\\\\\\\\\\ 更新服务器代码 \\\\\\\\ \\\\\\\\\\\\\\\\\\\

class IphoneChat(Protocol): 
def connectionMade(self): 
    print "a client connected" 
    self.transport.setTcpNoDelay(True) 

    int = 0 

    while(True): 
     time.sleep(5) 
     msg = 'F#m' 
     b = (msg + '/').encode('utf-8') 
     msg2 = 'C' 
     c = (msg2 + '/').encode('utf-8') 

     if int==0: 
      self.transport.write(b) 
      int = 1 
     else: 
      self.transport.write(c) 
      int = 0 
      break 

\\\\\\\\\\\\\\\\\\\\\\\\ \\\ 更新的客户端代码 \\\\\\\\\\\\\\\\\\\\\\\\\\\

  socket = new Socket(dstAddress, dstPort); 

      Log.d("socket created", "socket created"); 

      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(
        1024); 
      byte[] buffer = new byte[1]; 

      int bytesRead; 
      InputStream inputStream = socket.getInputStream(); 

      /* 
      * notice: inputStream.read() will block if no data return 
      */ 
      //while ((bytesRead = inputStream.read(buffer)) != -1) { 

      while(true){ 
       Log.d("waiting", "waiting"); 
       while(!byteArrayOutputStream.toString().contains("/")){ 
        bytesRead = inputStream.read(buffer); 
        byteArrayOutputStream.write(buffer, 0, bytesRead); 
        Log.d("buffer", buffer.toString()); 
       } 

       //byteArrayOutputStream.write(buffer, 0, bytesRead); 
       response = byteArrayOutputStream.toString("UTF-8"); 
       response = response.substring(0, response.length()-1); 
       byteArrayOutputStream = new ByteArrayOutputStream(
         1024); 

       Log.d("response", response); 



       //immediately run UIthread 
       runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         Log.d("printing", "printing"); 
         textResponse.setText(response); 
        } 
       }); 

      } 
+0

这实在是一个新问题,应该这样问(如果你的原始问题已经解决了,可以通过创建一个新问题,或者如果不是这样的话编辑这个问题)。但是,首先......你是否尝试禁用唠叨,正如我在对我的回答的评论中解释的那样,或者阅读维基百科解释它的文章? – abarnert 2014-10-31 19:41:44

相关问题