可以在Twisted TCP的同一连接中发送两个单独的数据吗?

时间:2014-10-30 21:32:52

标签: android python tcp twisted

我正在尝试在一个系统上工作,在该系统中,服务器将建立连接并实时向客户端发送多个数据。

为了模拟“实时”,我的服务器会一个接一个地发送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来完成它吗?

2 个答案:

答案 0 :(得分:1)

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

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

在Twisted中,您通常在Protocol类中执行此操作;事实上,这几乎就是协议的重点。

有关在TCP之上构建简单协议的一些常规(非Twisted)示例,请参阅Sockets are byte streams, not message streams。但是,如果你的消息是纯文本而其中没有可能的换行符,那么一个简单的(并且易于人工调试!)一个,只是将每个消息发送到一个单独的行,就像写消息时一样。一个文件:

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);
            }
        });
    }
}

*实际上,由于数据包和缓冲区等的工作方式,您有时会看到读取与写入完全匹配 - 尤其是在本地主机上测试时,在卸载的机器上,写入之间存在长距离等等。但你不能在实际条件下依赖它。

答案 1 :(得分:0)

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

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

我的服务器实际上每隔5秒发送2个数据: 连接开始后,等待5秒,发送第1个数据,等待5秒,发送第2个数据。 但是,在我的Android客户端,它在顺序打印两个数据之前等待了10秒。

我在这里做错了,以至于无法实时打印它们吗?

此致

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

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);
                    }
                });

            }