码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-08-15 00:05:06      阅读:365      评论:0      收藏:0      [点我收藏+]

标签:

上次写了一篇文章  基于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#端代码(不包含通信框架)

 

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

标签:

原文地址:http://www.cnblogs.com/csdev/p/4731463.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!