NetworkComms c#通信框架与Java的Netty框架通信 解决粘包问题

Sunny5816 2015-08-15 07:35:29
上次写了一篇文章 基于networkcomms V3通信框架的c#服务器与java客户端进行通信之Protobuf探讨 其中没有解决粘包问题,抛砖引玉,文章得到了失足程序员 老师的点评,并给出了解决方案:[最多评论]java netty socket库和自定义C#socket库利用protobuf进行通信完整实例(10/591) »

于是马上开始学习,并把c#服务器端换成了我比较熟悉的networkcomms v3 c#通信框架(商业版,本文并不提供) ,以方便与已经存在的系统进行整合。

客户端没有改动,依旧使用 失足程序员 老师的netty客户端,proto的message文件也没有变化,具体可以参见上面的文章

服务器端

由于networkcomms支持与其他语言的通信,所以改动很少

修改networkcomms v3框架的源文件

《1》修改ConnectionIncomingData.cs文件:
 topPacketHeader = new PacketHeader(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged), packetBuilder.TotalBytesCached);

改为:
//java
//指定包头大小为4个字节
packetHeaderSize = 4;

if (packetBuilder.TotalBytesCached < packetHeaderSize)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... require " + packetHeaderSize + " bytes for packet header, only " + packetBuilder.TotalBytesCached + " bytes cached.");

packetBuilder.TotalBytesExpected = packetHeaderSize;
return;
}
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... deserializing header using " + packetHeaderSize + " bytes, " + packetBuilder.TotalBytesCached + " bytes cached.");

//根据前4个字节,获取包体的大小
int expectDateCount = packetBuilder.GetUnmangeHeaderByte(0, 4);

//已经接收到足够的包头的大小
topPacketHeader = new PacketHeader(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged), expectDateCount);

《2》修改PacketBuilder.cs文件

添加如下方法:
//返回消息头字节数据
public int GetUnmangeHeaderByte(int startIndex, int length)
{
lock (Locker)
{
byte[] returnArray = new byte[length];
int runningTotal = 0, writeTotal = 0;
int startingPacketIndex;

int firstPacketStartIndex = 0;
//First find the correct starting packet
for (startingPacketIndex = 0; startingPacketIndex < packets.Count; startingPacketIndex++)
{
if (startIndex - runningTotal <= packetActualBytes[startingPacketIndex])
{
firstPacketStartIndex = startIndex - runningTotal;
break;
}
else
runningTotal += packetActualBytes[startingPacketIndex];
}

//Copy the bytes of interest
for (int i = startingPacketIndex; i < packets.Count; i++)
{
if (i == startingPacketIndex)
{
if (length > packetActualBytes[i] - firstPacketStartIndex)
//If we want from some starting point to the end of the packet
Buffer.BlockCopy(packets[i], firstPacketStartIndex, returnArray, writeTotal, packetActualBytes[i] - firstPacketStartIndex);
else
{
//We only want part of the packet
Buffer.BlockCopy(packets[i], firstPacketStartIndex, returnArray, writeTotal, length);
writeTotal += length;
break;
}

writeTotal = packetActualBytes[i] - firstPacketStartIndex;
}
else
{
//We are no longer on the first packet
if (packetActualBytes[i] + writeTotal >= length)
{
//We have reached the last packet of interest
Buffer.BlockCopy(packets[i], 0, returnArray, writeTotal, length - writeTotal);
writeTotal += length - writeTotal;
break;
}
else
{
Buffer.BlockCopy(packets[i], 0, returnArray, writeTotal, packetActualBytes[i]);
writeTotal += packetActualBytes[i];
}
}
}

if (writeTotal != length) throw new Exception("Not enough data available in packetBuilder to complete request. Requested " + length.ToString() + " bytes but only " + writeTotal.ToString() + " bytes were copied.");

//返回字节数组
//由于java返回的字序不同,需要进行反转
Array.Reverse(returnArray);
return BitConverter.ToInt32(returnArray, 0);
//双方统一编码 暂时不使用
//return bytesToInt(returnArray,0);

}

}

服务器端主要的处理方法是:
//array  收到字节
NetworkComms.AppendGlobalIncomingUnmanagedPacketHandler((header, connection, array) =>
{
//接收的字节数组中,前四个字节是消息类型 后面的字节对应相应的类
MemoryStream ms1 = new MemoryStream(array);

BinaryReader buffers = new BinaryReader(ms1, UTF8Encoding.Default);
//消息类型
int msgID = ReadInt(buffers.ReadBytes(4));

byte[] body = buffers.ReadBytes(array.Length - 4);

MemoryStream ms2 = new MemoryStream();
ms2.Write(body, 0, body.Length);
ms2.Position = 0;


if (msgID == (int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ReqLogin)
{
Sz.Test.ProtoMessage.TestMessage.ReqLoginMessage msg = Serializer.Deserialize<Sz.Test.ProtoMessage.TestMessage.ReqLoginMessage>(ms2);
//在服务器上做一下记录
LogInfo.LogMessage("数据收到" + msg.userName, "客户端发来的数据123");

Sz.Test.ProtoMessage.TestMessage.ResTipMessage tip = new Sz.Test.ProtoMessage.TestMessage.ResTipMessage();
if (msg.userName == "admin" && msg.userPwd == "admin")
{
tip.msg = "服务器端返回消息 登录完成";
}
else
{
tip.msg = "服务器端返回消息 用户名或者密码错误";
}

//序列化要返回的ResTipMessage类为字节数组
byte[] buffer = Serialize(tip);
//加上消息头和消息类型
byte[] resBuffer = Encoder(new Sz.Network.SocketPool.SocketMessage((int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ResTip, buffer));

//返回给客户端
connection.SendUnmanagedBytes(resBuffer);



}
else if (msgID == (int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ReqChat)
{

//构建消息
Sz.Test.ProtoMessage.TestMessage.ReqChatMessage chatMessage = Serializer.Deserialize<Sz.Test.ProtoMessage.TestMessage.ReqChatMessage>(ms2);
//在服务器上做一下记录
LogInfo.LogMessage("聊天消息数据收到" + chatMessage.msg, "客户端发来的聊天消息");
Sz.Test.ProtoMessage.TestMessage.ResChatMessage chat = new Sz.Test.ProtoMessage.TestMessage.ResChatMessage();
chat.msg = "服务器发送的聊天消息";

//序列化要返回的ResTipMessage类为字节数组
byte[] buffer = Serialize(chat);
//加上消息头和消息类型
byte[] resBuffer = Encoder(new Sz.Network.SocketPool.SocketMessage((int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ResChat, buffer));

//返回给客户端
connection.SendUnmanagedBytes(resBuffer);

}
ms1.Close();
ms2.Close();
buffers.Close();
ms1.Dispose();
ms2.Dispose();
buffers.Dispose();

});

//由失足程序员老师提供

public byte[] Encoder(SocketMessage msg)
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms, UTF8Encoding.Default);
byte[] msgBuffer = msg.MsgBuffer;

if (msgBuffer != null)
{
//数据长度 转为java可以识别
bw.Write(WriterInt(msgBuffer.Length + 4));
//消息类型 转为java可以识别
bw.Write(WriterInt(msg.MsgID));
bw.Write(msgBuffer);
}
else
{

bw.Write(WriterInt(0));

}
bw.Close();
ms.Close();
bw.Dispose();
ms.Dispose();
return ms.ToArray();


}

上面代码中使用的代码很多都是直接借用 失足程序员 老师的代码

服务器端界面:



为便于测试,收到的消息,写入了文本文件:





客户端界面:





java端代码 (由失足程序员老师开发,我下载下来的不能直接编译,把有一个命名空间大写换成小写就好了,这里把改动的发上来)

c#端代码(不包含通信框架)

...全文
277 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧