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

问题描述

上次写了一篇文章其中没有解决粘包问题,抛砖引玉,文章得到了失足程序员老师的点评,并给出了解决方案:»于是马上开始学习,并把c#服务器端换成了我比较熟悉的networkcommsv3c#通信框架(商业版,本文并不提供),以方便与已经存在的系统进行整合。客户端没有改动,依旧使用失足程序员老师的netty客户端,proto的message文件也没有变化,具体可以参见上面的文章服务器端由于networkcomms支持与其他语言的通信,所以改动很少修改networkcommsv3框架的源文件《1》修改ConnectionIncomingData.cs文件:topPacketHeader=newPacketHeader(Enum.GetName(typeof(ReservedPacketType),ReservedPacketType.Unmanaged),packetBuilder.TotalBytesCached);

改为://java//指定包头大小为4个字节packetHeaderSize=4;if(packetBuilder.TotalBytesCached<packetHeaderSize){if(NetworkComms.LoggingEnabled)NetworkComms.Logger.Trace("...require"+packetHeaderSize+"bytesforpacketheader,only"+packetBuilder.TotalBytesCached+"bytescached.");packetBuilder.TotalBytesExpected=packetHeaderSize;return;}if(NetworkComms.LoggingEnabled)NetworkComms.Logger.Trace("...deserializingheaderusing"+packetHeaderSize+"bytes,"+packetBuilder.TotalBytesCached+"bytescached.");//根据前4个字节,获取包体的大小intexpectDateCount=packetBuilder.GetUnmangeHeaderByte(0,4);//已经接收到足够的包头的大小topPacketHeader=newPacketHeader(Enum.GetName(typeof(ReservedPacketType),ReservedPacketType.Unmanaged),expectDateCount);

《2》修改PacketBuilder.cs文件添加如下方法://返回消息头字节数据publicintGetUnmangeHeaderByte(intstartIndex,intlength){lock(Locker){byte[]returnArray=newbyte[length];intrunningTotal=0,writeTotal=0;intstartingPacketIndex;intfirstPacketStartIndex=0;//Firstfindthecorrectstartingpacketfor(startingPacketIndex=0;startingPacketIndex<packets.Count;startingPacketIndex++){if(startIndex-runningTotal<=packetActualBytes[startingPacketIndex]){firstPacketStartIndex=startIndex-runningTotal;break;}elserunningTotal+=packetActualBytes[startingPacketIndex];}//Copythebytesofinterestfor(inti=startingPacketIndex;i<packets.Count;i++){if(i==startingPacketIndex){if(length>packetActualBytes[i]-firstPacketStartIndex)//IfwewantfromsomestartingpointtotheendofthepacketBuffer.BlockCopy(packets[i],firstPacketStartIndex,returnArray,writeTotal,packetActualBytes[i]-firstPacketStartIndex);else{//WeonlywantpartofthepacketBuffer.BlockCopy(packets[i],firstPacketStartIndex,returnArray,writeTotal,length);writeTotal+=length;break;}writeTotal=packetActualBytes[i]-firstPacketStartIndex;}else{//Wearenolongeronthefirstpacketif(packetActualBytes[i]+writeTotal>=length){//WehavereachedthelastpacketofinterestBuffer.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)thrownewException("NotenoughdataavailableinpacketBuildertocompleterequest.Requested"+length.ToString()+"bytesbutonly"+writeTotal.ToString()+"byteswerecopied.");//返回字节数组//由于java返回的字序不同,需要进行反转Array.Reverse(returnArray);returnBitConverter.ToInt32(returnArray,0);//双方统一编码暂时不使用//returnbytesToInt(returnArray,0);}}

服务器端主要的处理方法是://array收到字节NetworkComms.AppendGlobalIncomingUnmanagedPacketHandler((header,connection,array)=>{//接收的字节数组中,前四个字节是消息类型后面的字节对应相应的类MemoryStreamms1=newMemoryStream(array);BinaryReaderbuffers=newBinaryReader(ms1,UTF8Encoding.Default);//消息类型intmsgID=ReadInt(buffers.ReadBytes(4));byte[]body=buffers.ReadBytes(array.Length-4);MemoryStreamms2=newMemoryStream();ms2.Write(body,0,body.Length);ms2.Position=0;if(msgID==(int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ReqLogin){Sz.Test.ProtoMessage.TestMessage.ReqLoginMessagemsg=Serializer.Deserialize<Sz.Test.ProtoMessage.TestMessage.ReqLoginMessage>(ms2);//在服务器上做一下记录LogInfo.LogMessage("数据收到"+msg.userName,"客户端发来的数据123");Sz.Test.ProtoMessage.TestMessage.ResTipMessagetip=newSz.Test.ProtoMessage.TestMessage.ResTipMessage();if(msg.userName=="admin"&&msg.userPwd=="admin"){tip.msg="服务器端返回消息登录完成";}else{tip.msg="服务器端返回消息用户名或者密码错误";}//序列化要返回的ResTipMessage类为字节数组byte[]buffer=Serialize(tip);//加上消息头和消息类型byte[]resBuffer=Encoder(newSz.Network.SocketPool.SocketMessage((int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ResTip,buffer));//返回给客户端connection.SendUnmanagedBytes(resBuffer);}elseif(msgID==(int)Sz.Test.ProtoMessage.TestMessage.Proto_Login.ReqChat){//构建消息Sz.Test.ProtoMessage.TestMessage.ReqChatMessagechatMessage=Serializer.Deserialize<Sz.Test.ProtoMessage.TestMessage.ReqChatMessage>(ms2);//在服务器上做一下记录LogInfo.LogMessage("聊天消息数据收到"+chatMessage.msg,"客户端发来的聊天消息");Sz.Test.ProtoMessage.TestMessage.ResChatMessagechat=newSz.Test.ProtoMessage.TestMessage.ResChatMessage();chat.msg="服务器发送的聊天消息";//序列化要返回的ResTipMessage类为字节数组byte[]buffer=Serialize(chat);//加上消息头和消息类型byte[]resBuffer=Encoder(newSz.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();});

//由失足程序员老师提供publicbyte[]Encoder(SocketMessagemsg){MemoryStreamms=newMemoryStream();BinaryWriterbw=newBinaryWriter(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();returnms.ToArray();}

上面代码中使用的代码很多都是直接借用 失足程序员 老师的代码服务器端界面:为便于测试,收到的消息,写入了文本文件:客户端界面:(由失足程序员老师开发,我下载下来的不能直接编译,把有一个命名空间大写换成小写就好了,这里把改动的发上来)(不包含通信框架)

时间: 2025-01-23 12:01:03

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

javascript-js客户端与c#的后台服务进行websocket通信。后台服务对于多帧接收的粘包等问题。

问题描述 js客户端与c#的后台服务进行websocket通信.后台服务对于多帧接收的粘包等问题. 客户端网页是纯的javascript写的,后台服务是c#语言写的. 于是,用websocket通信的时候问题就来了,后台服务在接收客户端网页的多帧数据的时候,出现了粘包问题. 具体来说就是: 1.后台服务使用异步的 client.BeginReceive(RecvData, 0, RecvData.Length, SocketFlags.None,new AsyncCallback(AcceptC

java中Netty框架中的@Skip使用详解

最近在学习Netty框架,对着教程上写了个简单的netty应用,可是死活调试不成功,对着程序跟教程上看了几遍也找不到原因,后来又重新写了一遍,服务端程序终于调试成功,原因出在了那个@Skip注释上了,代码如下: package com.chris.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.n

Java的MyBatis框架中对数据库进行动态SQL查询的教程_java

其实MyBatis具有的一个强大的特性之一通常是它的动态 SQL 能力. 如果你有使用 JDBC 或其他 相似框架的经验,你就明白要动态的串联 SQL 字符串在一起是十分纠结的,确保不能忘了空格或在列表的最后省略逗号.Mybatis中的动态 SQL 可以彻底处理这种痛苦.对于动态SQL,最通俗简单的方法就是我们自己在硬编码的时候赋予各种动态行为的判断,而在Mybatis中,用一种强大的动态 SQL 语 言来改进这种情形,这种语言可以被用在任意映射的 SQL 语句中.动态 SQL 元素和使用 JS

Java线程池框架核心代码解析_java

前言多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过程和执行过程解耦开来,并用Runnable表示任务. 下面,我们来分析一下 Java 线程池框架的实现ThreadPoolExecutor. 下面的分析基于JDK1.7 生命周期ThreadPoolExecutor中,使用CAPACITY的高3位来表示运行状态,分别是:  1.RUNNING:接收新任

Java持久层框架MyBatis简单实例_java

什么是Mybatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .iBATIS一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架.iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO). MyBatis 本是apache的一个开源项目iB

handler-android使用netty框架与PC服务端通信,接收到内容与发送内容不同

问题描述 android使用netty框架与PC服务端通信,接收到内容与发送内容不同 例如PC端发送888,android端却收到888后面还跟着一大串以前测试时候的内容, 部分代码如下,复制时括号有些错乱请勿在意: Bootstrap configureBootstrap(Bootstrap b, EventLoopGroup g) { b.group(g) .channel(NioSocketChannel.class) .remoteAddress(parements.getString(

让JAVA 和 .NET框架共存

.net框架 原创作者:Ashish Banerjee 翻译整理:51DOTNET CLUB(WWW.51DOTNET.COM)SLASH 目的:对JAVA与.NET框架共存的可能性做一个评估 目标受众:JAVA程序员和系统工程师    提要:        首先是对JAVA 和 .NET平台的构成做一个分析,然后是我个人对JAVA如何形成的一个认识,接着是分析微软和SUN之间的合作与分歧,最后是JAVA与.NET合作的前景.      我个人强烈认为JAVA与.NET将在不久的未来逐步的统一起

是否能让JAVA 和 .NET框架共存(转)

.net框架 原创作者:Ashish Banerjee 翻译整理:51DOTNET CLUB(WWW.51DOTNET.COM)SLASH 目的:对JAVA与.NET框架共存的可能性做一个评估 目标受众:JAVA程序员和系统工程师    提要:        首先是对JAVA 和 .NET平台的构成做一个分析,然后是我个人对JAVA如何形成的一个认识,接着是分析微软和SUN之间的合作与分歧,最后是JAVA与.NET合作的前景.      我个人强烈认为JAVA与.NET将在不久的未来逐步的统一起

Jt —— 面向Java模式的框架

概述 Jt 是一种用于快速实现 Java 应用程序的设计模式框架.Jt 在许多大的任务关键型系统中有应用.该框架实现以下目标: 框架架构基于一个消息传送设计模式:框架组件能够交互信息并通过发送.接收和处理消息执行计算.一个消息传送 API 具有简易性.强大的封装性和松耦合特性:可以使用一个 "lego/messaging" 架构将框架组件交换地插入复杂的框架应用程序中.可对框架消息执行同步或异步处理.框架充分利用消息设计模式/API 的功能和简易性. 设计模式框架使用消息传送来实现和/