标签:
这几天在博客园上看到好几个写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 /// 写入大端序的int
13 /// </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 }