使用套接字时如何考虑消息被分解?

How do I account for messages being broken up when using sockets?

我的设计

我正在使用套接字来实现聊天服务器。

客户端使用Java的java.net.Socket and BufferedReader从服务器读取消息。

服务器端使用Php的socket_read()从客户端获取消息。

它使用 Php 的 socket_write() 从服务器发送消息。 socket_write() 不保证会写出整个原始消息,这意味着我可能需要多次调用它才能发送出整个原始消息。

(在设计方面,客户端向服务器发送消息,服务器将这些消息重新路由到适当的客户端。)

担忧

我担心的是一条消息可能会被分解成几条较小的消息。因此,当服务器或客户端读取传入消息时,它实际上可能是原始消息的片段。

问题

这是我需要考虑的事情吗? 如果是,如何?

可能的解决方案

现在我正在考虑使用字节填充(这是一种网络技术,可以将字节插入到原始消息中,作为标志,在发送消息之前标记消息的开始和结束)。

如果您需要应用程序级别的消息,那么您必须在应用程序级别实现它们。有几种常见的做法:

1) 使用固定长度的消息。

2) 在每条消息前加上长度。

3) 使用自然不会出现在您的邮件中的 'end of message' 标记。

4) 使用 'end of message' 标记,如果它出现在您的消息中则将其转义。

是的,这是您需要在协议中处理的事情。

这里最典型的两种方法是:

  1. 制定协议 line-oriented。用换行符终止每条消息,并且在看到换行符之前不要将一行视为完整。当然,这取决于消息中不会自然出现的换行符。

    一些使用这种方法的协议包括 SMTP、IMAP 和 IRC。

  2. 在其header中包含消息的长度,以便您知道要读取多少数据。

    使用这种方法的一些协议包括 HTTP(在 Content-Length header 中)和 TLS,以及许多 low-level 协议,例如 IP。

如果您不确定采用哪种方法,第二种方法实施起来要容易得多,并且不会对您使用的数据施加任何限制。一个简单的实现可能只是将字节数存储为压缩整数,并且可能类似于以下伪代码:

send_data(dat):
    send(length of dat as packed integer)
    send(dat)

recv_data():
    size = recv(size of packed integer)
    return recv(buffer)

(此代码假定抽象 send()recv() 方法将阻塞,直到发送或接收整个消息。当然,您的代码必须使此工作适当。)