在Twisted TCP中可以在同一连接中发送两个独立数据吗?
我正在尝试建立一个系统,让服务器能够连接并实时向客户端发送多个数据。
为了模拟“实时”,我的服务器会每隔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 个回答
其实我根据@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);
}
});
}
基本上,是的,因为TCP无法做到你提到的那样。TCP是一个字节流,而不是消息流。对于TCP来说,你发送的字节是F
、#
、m
和C
,而客户端如何读取这些字节,比如是先读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);
}
});
}
}
* 实际上,由于数据包和缓冲区等工作的方式,有时你会看到读取的内容与写入的内容完全匹配——特别是在本地测试、在空闲的机器上、写入之间有长时间间隔等情况下。但在真实环境中,你不能依赖这一点。