因此,您在学习网络时会遇到的一件事就是失去同步。例如,当您“硬编码”一组客户端-服务器交互时,就会发生这种情况;
server sends 2 byte status code
client receives 2 byte status code
client responds with 4 byte operation code
如果由于某种原因有一个错误导致此交互的任何部分不能完全按照需要发生,那么程序的其余部分将失败,因为所有网络交互现在都不同步。客户端可能会读取它认为代表字符串的一组字节,而实际上服务器正在发送一个 int,等等。最糟糕的是,您可能会发现您的主网络线程死锁,因为客户端和服务器都在同时等待输入。
对于一个较大的项目,其中肯定存在错误,如果您以这种风格编写代码,这将发生很多。出于这个原因,我们有一个叫做中间件的东西。
一个非常常见且灵活的中间件范例是TLV 消息协议。您将实现一些简单的类(在半 java 伪代码中);
TLVMessage
int type;
byte[] value;
TLVPusher implements Runnable
OutputStream out;
Queue<TLVMessage> messages;
run() {
while(true) {
//poll and write front of queue to out (INCLUDING value.length!)
}
}
TLVReader implements Runnable
InputStream in;
Queue<TLVMessage> messages;
run() {
while(true) {
//read message from in and add to queue
}
}
现在您有两个线程在客户端运行,两个线程在服务器上运行。每一端都有自己的Pusher
和Reader
。需要注意的重要一点是,因为您将长度字段写入输出流,所以读取器始终知道它需要读取多少字节。所以即使一条消息被错误地序列化了,它的长度仍然是正确的,并且下一条消息总是会从第一个字节到最后一个字节被正确读取。这样你的程序就永远不会不同步。
您只需将TLVMessage
对象添加到pusher.queue
,它们就会到达reader.queue
套接字另一端的 。reader.messages.size()
然后,您可以按其type
字段处理消息(在另一个,第三个线程中)。
您无需担心事情发生的顺序,您有一个强大的机制可以在客户端和服务器之间在两个方向上传递消息。您已经抽象出繁琐的网络内容,并且可以继续编码。
当然,有一些库可以为您完成所有这些工作,但在我看来,了解其中的方式和原因总是值得的。