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长度的字节[],开头是我需要的数据,后面还有几个零,肯定解码不了
我正在尝试完成一个具有网络功能的 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长度的字节[],开头是我需要的数据,后面还有几个零,肯定解码不了