1

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

当我从客户端创建套接字并将消息发送到服务器时,一切顺利。服务器回复时出现问题:在客户端,异步方法已准备好接收消息。当我简单地从服务器发送一个字符串格式的消息时,该方法能够得到它。但是当我用一个 4 长度的字节 [] 替换消息时,它是从 Protobuf 消息对象编码的,客户端只是显示它没有收到任何内容。

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

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

通道读取()

@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();
}

通道读取完成()

@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。

对于“为什么客户端套接字什么也没收到”的任何想法以及解决方案,我将不胜感激。由于我来自亚洲,您的回复和我的回复可能会有时间差,而且英语不是我的母语。不过,我会尽量及时回复。非常感谢!

4

1 回答 1

0

好的..我终于自己解决了

1.使用“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 Channel Pipeline 中注册了“new ProtobufEncoder()”,我就可以使用“return replyBuilder.build()”——这是正确的。

2.在“static byte[] result = new byte[1024];”中,接收消息的长度随便定义,没关系——直到真正接收到消息。

接收消息时,我总是首先将消息字节复制到一个具有正确长度的新字节[] - 否则只会有一个 1024 长度的字节[],开头是我需要的数据,后面跟着几个零,这肯定无法解码。

于 2019-08-03T10:02:24.700 回答