Netty 服务器发送一个由 Protobuf 编码的 byte[],但 C# 客户端 Socket.Receive 一直为 0

Netty server send a byte[] encoded by Protobuf, but C# client Socket.Receive keeps being 0

我正在尝试完成一个具有网络功能的 Unity 游戏演示,使用 C# 编写客户端程序,Java 编写服务器程序。 具体来说,服务器通信是由Netty实现的。 我还引入了 Protobuf,它可以帮助我定义消息协议。 由于我是服务器编程的新手,我的代码中还没有考虑处理 TCP 中的数据包合并和丢失。

当我从客户端创建套接字并向服务器发送消息时,一切顺利。 服务器回复时出现问题: 在客户端中,异步方法已准备好接收消息。当我只是从服务器发送一条字符串格式的消息时,该方法能够获取它。 但是当我用从 Protobuf Message 对象编码的 4-length byte[] 替换消息时,客户端只显示它没有收到任何东西。

当我在服务器控制台打印我发送的内容时,它是这样的: 00001000 00000001 00010000 00000001

我的服务器代码覆盖了 Netty 的 channelRead 和 channelReadComplete 函数。 在channelRead中,调用了ChannelHandlerContext.write将消息写入传输缓存。 而在channelReadComplete中,调用了ChannelHandlerContext.flush,消息终于可以发送了。

channelRead()

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
   Request.MsgPack msgPack = (Request.MsgPack) msg;
   Request.MsgPack.MsgType type = msgPack.getType();
   switch (type)
   {
       case GetServerState:
            final Request.GetServerState gssbody = msgPack.getGetServerState();
            System.out.println("收到类型为" + type + "的消息,内容为:" +
                      "\nrequestId = " + gssbody.getRequestId()
                );
            byte[] bytes = ServerStateManager.getState(gssbody.getRequestId());
            ctx.write(bytes);
            break;

getState():包括 Protobuf 编码过程

public static byte[] getState(int requestId)
{
     ReturnServerState.Message.Builder replyBuilder = ReturnServerState.Message.newBuilder();
     replyBuilder.setRequestId(requestId);
     replyBuilder.setIsIdle(new ServerStateManager().isIdle());
     return replyBuilder.build().toByteArray();
}

channelReadComplete()

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
   try
   {
      ctx.flush();
   }
   finally
   {
      ctx.close();
   }
}

客户代码:

public class ShortLink
{
    Socket clientSocket = null;
    static byte[] result = new byte[1024];

    Task ReceiveAsync<T>(string ip, int port)
    {
        return Task.Run(() =>
        {
           T component = default(T);
           while (clientSocket.Receive(result) == 0)
           {
               break;

ReceiveAsync 的调用方式为:

await ReceiveAsync<ReturnServerState>(ip, port);

当我发现clientSocket.Receive(result)总是输出0时,我试着记录result[0]、result[1]、result[2]、result[3],像这样:

Debug.Log(Convert.ToString(result[0]) + ", " +
          Convert.ToString(result[1]) + ", " +
          Convert.ToString(result[2]) + ", " +
          Convert.ToString(result[3]));

日志变为 0,0,0,0。

如有任何想法 "why the client socket received nothing" 和解决方案,我将不胜感激。 由于我来自亚洲,您的回复和我的回复之间可能存在时间差,而且英语不是我的母语。不过,我会尽力及时回复。 非常感谢!

好的..终于自己解决了

1.The 用法 "return replyBuilder.build().toByteArray()" 是错误的,因为 ProtoEncoder 已经为我做了 toByteArray():

public class ProtobufEncoder extends MessageToMessageEncoder<MessageLiteOrBuilder> {
    public ProtobufEncoder() {
    }

    protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List<Object> out) throws Exception {
        if (msg instanceof MessageLite) {
            out.add(Unpooled.wrappedBuffer(((MessageLite)msg).toByteArray()));
        } else {
            if (msg instanceof Builder) {
                out.add(Unpooled.wrappedBuffer(((Builder)msg).build().toByteArray()));
            }

        }
    }
}

所以一旦我在 Netty 通道管道中注册 "new ProtobufEncoder()",我就可以使用 "return replyBuilder.build()" - 这是正确的。

2.In "static byte[] result = new byte[1024];", 接收消息的长度是随便定义的,无所谓- 直到真正接收到消息。

收到消息时,我总是先将消息字节复制到一个正确长度的新字节[] - 或者只有一个1024长度的字节[],开头是我需要的数据,后面还有几个零,肯定解码不了