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

0 投票
2 回答
504 浏览
提问于 2025-04-30 17:55

我正在尝试建立一个系统,让服务器能够连接并实时向客户端发送多个数据。

为了模拟“实时”,我的服务器会每隔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)

我的安卓客户端会接收到这些数据,并立即一个接一个地打印出来。

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

其实我根据@abarnert的建议对代码做了一些修改。

现在我的客户端可以把字符分开打印出来了。不过问题是,它不是实时打印,而是等所有数据都发送完了才按顺序打印出来,但中间有毫秒的差别。

我的服务器每5秒会发送2条数据,具体是这样的:一旦连接开始,先等5秒,发送第一条数据,再等5秒,发送第二条数据。

但是在我的安卓客户端上,它等了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);
                    }
                });

            }
1

基本上,是的,因为TCP无法做到你提到的那样。TCP是一个字节流,而不是消息流。对于TCP来说,你发送的字节是F#mC,而客户端如何读取这些字节,比如是先读3个字节再读1个字节,还是一次性读4个字节,或者是2个字节再2个字节,这完全是随意的。

但这并不意味着你必须使用UDP。如果你想通过TCP发送独立的消息,你只需要自己想办法来分隔这些消息,比如加上长度前缀、结束符或者分隔符,或者使用一种自我标识的消息格式等等。

在Twisted中,通常是在Protocol类中处理这个问题;实际上,这就是协议的主要目的。

你可以查看套接字是字节流,而不是消息流,里面有一些构建简单协议的通用(非Twisted)示例。但有一种非常简单(而且容易调试)的方式,如果你的消息是纯文本且其中没有可能的换行符,那就是把每条消息放在单独的一行,就像你写文件时那样:

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

* 实际上,由于数据包和缓冲区等工作的方式,有时你会看到读取的内容与写入的内容完全匹配——特别是在本地测试、在空闲的机器上、写入之间有长时间间隔等情况下。但在真实环境中,你不能依赖这一点。

撰写回答