简体   繁体   English

将套接字用于TCP上的自定义协议

[英]Using a socket for a custom protocol over tcp

My protocol looks like this: 我的协议如下所示:
[size: UInt16][Channel: Uint16][Protobuff packet]
The problem is, it is my understanding that tcp does not guarantee that an the entire packet as sent by the user will arrive all in one. 问题是,据我了解,tcp不能保证用户发送的整个数据包将一并到达。 Therefore, there could technically be a situation, where I don't get the entire message at once, or even that a part of another message is sent with this message. 因此,从技术上讲,可能会出现这样的情况,即我无法一次获得全部消息,甚至无法与该消息一起发送另一条消息的一部分。

It was quite easy, when I was just assuming that I would be getting the entire message at once, because I just read the first 2 bytes from the stream, made a buffer with the proper size and called read(buffer) however if I start thinking about the possibility of cut-off, I am not sure how to handle it. 这很简单,当我只是假设我将立即获取整个消息时,因为我只是从流中读取前2个字节,制作了一个具有适当大小的read(buffer)但是如果我开始,则称为read(buffer)考虑到停产的可能性,我不确定如何处理。 I tried the following: 我尝试了以下方法:

    val input = clientSocket.getInputStream()
    while (!isStopped) {
        val bufferedInputStream: BufferedInputStream = BufferedInputStream(input)
        val dataInputStream: DataInputStream = DataInputStream(bufferedInputStream)
        var messageSize: Int? = null
        var channel: Int? = null
        while (true) {
            if (messageSize == null) {
                messageSize = dataInputStream.readUnsignedShort()
            }
            if (channel == null) {
                channel = dataInputStream.readUnsignedShort()
            }
            var bytesRead = 0
            var didReachEnd = false
            val buffer = ByteArray(messageSize - 4)
            while (bytesRead != -1 && bytesRead != messageSize) {
                val temp = dataInputStream.read(buffer)
                if (temp == -1) {
                    didReachEnd = true
                } else {
                    bytesRead += temp
                }
                Logger.debug(this::class.java, "bytes read: $bytesRead out of $messageSize")
            }
            val packet = ChatChannel.Packet.parseFrom(buffer)
            print(packet.chatMessage.messageText)
        }
    }

And I attempt to imitate a message splitting by doing the following from my client socket: 我尝试通过从客户端套接字执行以下操作来模仿消息拆分:

        val chatPacket = ChatChannel.Packet.newBuilder().setChatMessage(chatChannel).build()
        val sendPacket = Packet(0,chatPacket.toByteArray())
        val sendPacketByteArray = sendPacket.toByteArray()


        val halfPoint = sendPacketByteArray.size/2
        val firstSlice = sendPacketByteArray.sliceArray(IntRange(0,halfPoint))
        val secondSlice = sendPacketByteArray.sliceArray(IntRange(halfPoint,halfPoint+(halfPoint%2)))

        s.getOutputStream().write(firstSlice)
        sleep(200)
        s.getOutputStream().write(secondSlice)

however my implementation just hangs in the following line in the second time its ran: 但是我的实现在第二次运行时就挂在了下面的行中:

bytesRead = dataInputStream.read(buffer,0,buffer.size)

I have a feeling I am missing some idea of how Sockets work, however every implementation I have seen, assumes that when the user is done sending he will close the connection, but in my case it's a chat program, so the user will only close the connection when were done chatting. 我有一种关于套接字如何工作的想法,但是我看到的每个实现都假定当用户发送完消息后,他将关闭连接,但在我的情况下,它是一个聊天程序,因此用户只会关闭完成聊天时的连接。

What I would like to achieve: 我想实现的目标:

  • read size 读取大小
  • read channel 读取频道
  • read bytes until the buffer of size is full 读取字节,直到缓冲区的size已满
  • parse message 解析消息
  • notify 通知
  • back to step 0 返回步骤0

Edit: After further research, I found that MessageLite library to work with protobuf in Java seems to have methods called writeDelimitedTo(outputStream) and parseDelimitedFrom(inputStream) so it seems most of the work has been done for me. 编辑:经过进一步研究,我发现可以在Java中与protobuf一起使用的MessageLite库似乎具有称为writeDelimitedTo(outputStream)和parseDelimitedFrom(inputStream)的方法,因此似乎大部分工作已为我完成。

Two things: 两件事情:

while (bytesRead != -1 && bytesRead != messageSize) {
                bytesRead = dataInputStream.read(buffer,0,buffer.size)

Seems strange to me. 对我来说似乎很奇怪。 You do no increment bytesRead. 您不增加bytesRead。 That is, if messageSize is 100 and you read 90 bytes in the first read, you will read 10 bytes in the second read. 也就是说,如果messageSize为100,并且在第一次读取中读取了90个字节,则在第二次读取中将读取10个字节。 You cannot simply say 'bytesRead += ...' since it might return -1. 您不能简单地说“ bytesRead + = ...”,因为它可能返回-1。 So you need another variable. 因此,您需要另一个变量。 Check if the return value is non-negative and then do the increment to a byteCounter. 检查返回值是否为非负数,然后对byteCounter进行递增。 This byteCounter needs to be compared to messageSize. 该byteCounter需要与messageSize比较。

Also: If all you are doing is providing multiple channels for Google protobuf messages, don't do it like this! 另外:如果您要做的只是为Google protobuf消息提供多个渠道,请不要这样做! Create a protobuf message that contains the protobuf message and a channel variable. 创建包含protobuf消息和通道变量的protobuf消息。 That should make life a lot easier. 那应该使生活更加轻松。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM