当前位置: 代码迷 >> C# >> 小弟我看不下去鸟。Java和C#的socket通信真的简单吗
  详细解决方案

小弟我看不下去鸟。Java和C#的socket通信真的简单吗

热度:68   发布时间:2016-05-05 03:07:09.0
我看不下去鸟。。。。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 [email protected] 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#妥协进行字节序反转操作~!

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

到此结束~!

 

18楼名山大川
有能运行的demo么? [email protected] 谢谢
17楼莫压枯枯地
用C写一个bsd socket库,通用,多好!
Re: 失足程序员
@莫压枯枯地,引用用C写一个bsd socket库,通用,多好!,通用哈哈,,问题是比如ios系统不支持库,如何是好?U3D做ios应用,如何支持C的代码呢?嘿嘿
16楼李奥霍克
我真是要笑死,还性能,大概博主的服务器CPU不是小端序
15楼澜紫癜青
你需要的是supersocket
14楼双鱼座
两位说的都不错,失足站在局部,及具体的问题上;fengniu是站在一般的角度。,OSI有7层协议,什么字节序啥的,都是会话层甚至应用层的协议,Socket其实不关心的,只关心传输层。试问,如果我都不知道你传来的是一个int,我如何关心你用什么方式排列字节?老的协议里采用IEEE传输浮点数的很少,用BCD的甚至ASCII的比较多,因为二进制协议的比较费解。
Re: 失足程序员
@双鱼座,引用两位说的都不错,失足站在局部,及具体的问题上;fengniu是站在一般的角度。,OSI有7层协议,什么字节序啥的,都是会话层甚至应用层的协议,Socket其实不关心的,只关心传输层。试问,如果我都不知道你传来的是一个int,我如何关心你用什么方式排列字节?老的协议里采用IEEE传输浮点数的很少,用BCD的甚至ASCII的比较多,因为二进制协议的比较费解。,顶你
13楼碎景
博主太作了吧,一面说让别人无视,一面又不断at别人...,socket就socket ,和c# java python有1毛钱关系?(最多半毛钱关系)
12楼破代码
UNIX网络编程卷1中就有提到网络字节序的问题,想了解的可以去看看,具体是哪一章我也忘记了。不必在此争论了。
11楼失足程序员
@Alvin,不管tcp还是udp传输都是你压入什么传输什么,不会改变的。
Re: Alvin
@失足程序员,那字节序其实是你协议要管的事情,和java,C#无关。,没有那个网关不跟一套协议的。
10楼奶爸.萱
都要考虑封包,字节序的吧???
Re: 失足程序员
@奶爸.萱,考虑的了啊?
9楼牛!
很期待楼主晒一下你的通讯框架
Re: 失足程序员
@牛!,引用很期待楼主晒一下你的通讯框架,之前的文章已经写过了,嘿嘿
8楼foreach_break
没有看得太懂。,,觉得楼主搞复杂了,也觉得楼主对java了解不够深入。 : ],,大端在实际表现中,有分文件大端、内存大端、网络传输大端多种...但本质是一样的。,,在网络传输中,发送方知道自己要发的数据是大端还是小端。,,序列化可以任意,你可以byte1 byte2 byte3 byte4也可以4 3 2 1,甚至可以1,3,2,4,这都不是事,只要,,接收方知道怎么反序列化就够了。,,另外没有消息“压栈”这个概念,在传输层,tcp层和ip层,包括下面的链路层和驱动层等,都只有压队列这个概念.,,另外,粘包的问题,如果熟悉帧定界的话就没有问题了,如果是tcp传输,这个协议是会保证字节按序到达,至于这个字节属于哪个包,是应用程序的事,,,[optional prefix] | op | len | data|,,按上面的方式来描述包就够了.,,判断是否完整的包,仅关注len拿到后,是否接收了len那么长的字节就可以解决所谓粘包了.,,have fun : ]
7楼风清气正
非常棒 学习了 很有价值
6楼程序员小U
难道不简单么 ^_^
5楼风清气正
希望楼主提供下测试代码,这样我们可以多学到一点
4楼fengniumaxi
考虑那么多?直接都是网络字节序就OK啊,和JAVA C#没关系的啊
Re: 失足程序员
@fengniumaxi,说了半天等于没说。
3楼Alvin
什么叫字节序,你难道不自定义协议?,你只考虑了java、c#,是不是还漏掉了TCP传输的字节序?
2楼arg
别急,都是人家练练手的,题目都是初步,浅析什么的。,还有那些什么鬼模式的。,你说你个读书笔记,一个demo发首页干嘛呢?,周周有,月月有。
Re: 失足程序员
@arg,引用别急,都是人家练练手的,题目都是初步,浅析什么的。,还有那些什么鬼模式的。,你说你个读书笔记,一个demo发首页干嘛呢?,周周有,月月有。,我。。。。。
1楼jiahuafu
使用silverFoxServer.net吧,,JAVA和C#语言实现SOCKET功能,,二种语言实现功能完全相同,
  相关解决方案