你的位置:首页 > ASP.net教程

[ASP.net教程]我看不下去鸟。。。。Java和C#的socket通信真的简单吗?


这几天在博客园上看到好几个写Java和C#的socket通信的帖子。但是都为指出其中关键点。

C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的。本人使用的是自己开发的一套组件。

Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty。游戏行业使用也是居多。

关于socket的底层写法,实在太多,我就不在BB。

这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序。而java使用的是大端序作为字节序。

也就是说比如一个int占用四个字节,java的字节序和c#的字节序是相反的,java的int四个字节第一个字节在数组的最后一个。C#是第一个。

也就是说如果java端正常发送一个int的字节序给C#,需要翻转一次端绪。反之也是一样的。一句话来概括的话就是高位在前还是低位在前的问题。

C#输出数字 int 4 的字节序。为了保证c#下面绝对是是int所以加入了强制int转化。默认的话可能是byte

java的默认输出,这里使用的是netty的默认框架。进行的int4的字节序输出

高位和低位表示法完全不同。

java下面如果传输字符串,那么必须要先把字符串转化成byte数组,然后获取数组长度,在字节序里面压入int表示的数组长度,然后在然如byte数组。不管你的字符串多长。

而C#也是相同做法。但是唯一不同的是数组的长度表示法不同。微软经过了字节压缩的。用字节的前7位表示长度。第8位表示下一个字节是否也是表示长度的字节,值需要与128位于。

从而减少字节的消耗。

现在一般如果我们在java和C#中无论是哪一个语言作为服务器。架设socket通信基准。其中另外一方都要妥协字节序反转问题。

大多数情况下我们也许通信的要求不高,或许把一些类或者参数通过json格式化以后传输给对方。但是在这一条消息的传输中,一般会有两个int需要字节序。最少也要一个字节序。

一个字节序int表示消息长度。另外一个字节序表示消息协议。

如果消息协议都放到json里面没有问题。但是消息长度是必不可少的。因为你需要知道在网络环境中,消息压栈,然后等待系统发出是有可能两条消息一同发送的。也或者消息发送后由于网络阻塞,前后相差好几秒的消息同一时间达到。

这就是所谓的粘包。

我这里就不表演了。

还有另外一种通信方式,就是通过protobuf进行字节序的序列化,和反序列,官方支持java,第三方支持C#。这个组件可以减少字节流。达到省流量,减少网络资源消耗的问题。

例如一个long的类型值是1常规发送需要8个字节,64位。发送。如果改用protobuf的话只需要1字节8位就能发送。

同样的问题,无论你使用哪一种序列化方式,都需要消息长度和消息协议号。

 

C#下面对int的反转读取。

 1     /// <summary> 2     /// 读取大端序的int 3     /// </summary> 4     /// <param name="value"></param> 5     public int ReadInt(byte[] intbytes) 6     { 7       Array.Reverse(intbytes); 8       return BitConverter.ToInt32(intbytes, 0); 9     }10 11     /// <summary>12     /// 写入大端序的int13     /// </summary>14     /// <param name="value"></param>15     public byte[] WriterInt(int value)16     {17       byte[] bs = BitConverter.GetBytes(value);18       Array.Reverse(bs);19       return bs;20     }

 

粘包问题解决。

C#代码

 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7  8 /** 9  *  10  * @author 失足程序员 11  * @Blog http://www.cnblogs.com/ty408/ 12  * @mail 492794628@qq.com 13  * @phone 13882122019 14  *  15 */ 16 namespace Sz.Network.SocketPool 17 { 18   public class MarshalEndian : IMarshalEndian 19   { 20  21     public enum JavaOrNet 22     { 23       Java, 24       Net, 25     } 26  27     public MarshalEndian() 28     { 29  30     } 31  32     public static JavaOrNet JN = JavaOrNet.Net; 33  34     /// <summary> 35     /// 读取大端序的int 36     /// </summary> 37     /// <param name="value"></param> 38     public int ReadInt(byte[] intbytes) 39     { 40       Array.Reverse(intbytes); 41       return BitConverter.ToInt32(intbytes, 0); 42     } 43  44     /// <summary> 45     /// 写入大端序的int 46     /// </summary> 47     /// <param name="value"></param> 48     public byte[] WriterInt(int value) 49     { 50       byte[] bs = BitConverter.GetBytes(value); 51       Array.Reverse(bs); 52       return bs; 53     } 54  55     //用于存储剩余未解析的字节数 56     private List<byte> _LBuff = new List<byte>(2); 57  58     //字节数常量一个消息id4个字节 59     const long ConstLenght = 4L; 60  61     public void Dispose() 62     { 63       this.Dispose(true); 64       GC.SuppressFinalize(this); 65     } 66  67     protected virtual void Dispose(bool flag1) 68     { 69       if (flag1) 70       { 71         IDisposable disposable = this._LBuff as IDisposable; 72         if (disposable != null) { disposable.Dispose(); } 73       } 74     } 75  76     public byte[] Encoder(SocketMessage msg) 77     { 78       MemoryStream ms = new MemoryStream(); 79       BinaryWriter bw = new BinaryWriter(ms, UTF8Encoding.Default); 80       byte[] msgBuffer = msg.MsgBuffer; 81  82       if (msgBuffer != null) 83       { 84         switch (JN) 85         { 86           case JavaOrNet.Java: 87             bw.Write(WriterInt(msgBuffer.Length + 4)); 88             bw.Write(WriterInt(msg.MsgID)); 89             break; 90           case JavaOrNet.Net: 91             bw.Write((Int32)(msgBuffer.Length + 4)); 92             bw.Write(msg.MsgID); 93             break; 94         } 95  96         bw.Write(msgBuffer); 97       } 98       else 99       {100         switch (JN)101         {102           case JavaOrNet.Java:103             bw.Write(WriterInt(0));104             break;105           case JavaOrNet.Net:106             bw.Write((Int32)0);107             break;108         }109       }110       bw.Close();111       ms.Close();112       bw.Dispose();113       ms.Dispose();114       return ms.ToArray();115     }116 117     public List<SocketMessage> Decoder(byte[] buff, int len)118     {119       //拷贝本次的有效字节120       byte[] _b = new byte[len];121       Array.Copy(buff, 0, _b, 0, _b.Length);122       buff = _b;123       if (this._LBuff.Count > 0)124       {125         //拷贝之前遗留的字节126         this._LBuff.AddRange(_b);127         buff = this._LBuff.ToArray();128         this._LBuff.Clear();129         this._LBuff = new List<byte>(2);130       }131       List<SocketMessage> list = new List<SocketMessage>();132       MemoryStream ms = new MemoryStream(buff);133       BinaryReader buffers = new BinaryReader(ms, UTF8Encoding.Default);134       try135       {136         byte[] _buff;137       Label_0073:138         //判断本次解析的字节是否满足常量字节数 139         if ((buffers.BaseStream.Length - buffers.BaseStream.Position) < ConstLenght)140         {141           _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position));142           this._LBuff.AddRange(_buff);143         }144         else145         {146           long offset = 0;147           switch (JN)148           {149             case JavaOrNet.Java:150               offset = ReadInt(buffers.ReadBytes(4));151               break;152             case JavaOrNet.Net:153               offset = buffers.ReadInt32();154               break;155           }156 157           //剩余字节数大于本次需要读取的字节数158           if (offset <= (buffers.BaseStream.Length - buffers.BaseStream.Position))159           {160             int msgID = 0;161             switch (JN)162             {163               case JavaOrNet.Java:164                 msgID = ReadInt(buffers.ReadBytes(4));165                 break;166               case JavaOrNet.Net:167                 msgID = buffers.ReadInt32();168                 break;169             }170             _buff = buffers.ReadBytes((int)(offset - 4));171             list.Add(new SocketMessage(msgID, _buff));172             goto Label_0073;173           }174           else175           {176             //剩余字节数刚好小于本次读取的字节数 存起来,等待接受剩余字节数一起解析177             buffers.BaseStream.Seek(ConstLenght, SeekOrigin.Current);178             _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position));179             this._LBuff.AddRange(_buff);180           }181         }182       }183       catch { }184       finally185       {186         buffers.Close();187         if (buffers != null) { buffers.Dispose(); }188         ms.Close();189         if (ms != null) { ms.Dispose(); }190       }191       return list;192     }193   }194 }

java netty

 1 /* 2  * To change this license header, choose License Headers in Project Properties. 3  * To change this template file, choose Tools | Templates 4  * and open the template in the editor. 5 */ 6 package sz.network.socketpool.nettypool; 7  8 import io.netty.buffer.ByteBuf; 9 import io.netty.buffer.Unpooled; 10 import io.netty.channel.ChannelHandlerContext; 11 import io.netty.handler.codec.ByteToMessageDecoder; 12 import java.nio.ByteOrder; 13 import java.util.ArrayList; 14 import java.util.List; 15 import org.apache.log4j.Logger; 16  17 /** 18  * 解码器 19 */ 20 class NettyDecoder extends ByteToMessageDecoder { 21  22   private static final Logger logger = Logger.getLogger(NettyDecoder.class); 23  24   private byte ZreoByteCount = 0; 25   private ByteBuf bytes; 26   private final ByteOrder endianOrder = ByteOrder.LITTLE_ENDIAN; 27   private long secondTime = 0; 28   private int reveCount = 0; 29  30   public NettyDecoder() { 31  32   } 33  34   ByteBuf bytesAction(ByteBuf inputBuf) { 35     ByteBuf bufferLen = Unpooled.buffer(); 36     if (bytes != null) { 37       bufferLen.writeBytes(bytes); 38       bytes = null; 39     } 40     bufferLen.writeBytes(inputBuf); 41     return bufferLen; 42   } 43  44   /** 45    * 留存无法读取的byte等待下一次接受的数据包 46    * 47    * @param bs 数据包 48    * @param startI 起始位置 49    * @param lenI 结束位置 50   */ 51   void bytesAction(ByteBuf intputBuf, int startI, int lenI) { 52     if (lenI - startI > 0) { 53       bytes = Unpooled.buffer(); 54       bytes.writeBytes(intputBuf, startI, lenI); 55     } 56   } 57  58   @Override 59   protected void decode(ChannelHandlerContext chc, ByteBuf inputBuf, List<Object> outputMessage) { 60     if (System.currentTimeMillis() - secondTime < 1000L) { 61       reveCount++; 62     } else { 63       secondTime = System.currentTimeMillis(); 64       reveCount = 0; 65     } 66  67     if (reveCount > 50) { 68       logger.error("发送消息过于频繁"); 69       chc.disconnect(); 70       return; 71     } 72  73     if (inputBuf.readableBytes() > 0) { 74       ZreoByteCount = 0; 75       //重新组装字节数组 76       ByteBuf buffercontent = bytesAction(inputBuf); 77       List<NettyMessageBean> megsList = new ArrayList<>(0); 78       for (;;) { 79         //读取 消息长度(short)和消息ID(int) 需要 8 个字节 80         if (buffercontent.readableBytes() >= 8) { 81           ///读取消息长度 82           int len = buffercontent.readInt(); 83           if (buffercontent.readableBytes() >= len) { 84             int messageid = buffercontent.readInt();///读取消息ID 85             ByteBuf buf = buffercontent.readBytes(len - 4);//读取可用字节数; 86             megsList.add(new NettyMessageBean(chc, messageid, buf.array())); 87             //第二次重组 88             if (buffercontent.readableBytes() > 0) { 89               bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes()); 90               buffercontent = Unpooled.buffer(); 91               buffercontent.writeBytes(bytes); 92               continue; 93             } else { 94               break; 95             } 96           } 97           ///重新设置读取进度 98           buffercontent.setIndex(buffercontent.readableBytes() - 2, inputBuf.readableBytes()); 99         }100         ///缓存预留的字节101         bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes());102         break;103       }104       outputMessage.addAll(megsList);105     } else {106       ZreoByteCount++;107       if (ZreoByteCount >= 3) {108         //todo 空包处理 考虑连续三次空包,断开链接109         logger.error("decode 空包处理 连续三次空包");110         chc.close();111       }112     }113   }114 }

这是我封装的部分代码,因为现目前公司的开发组织架构为,java是服务器端。U3D 使用C#是客户端开发。所以考虑性能问题,是C#妥协进行字节序反转操作~!

就不在添加调试和测试代码和结果因为觉得没多少意义~!

到此结束~!