这几天在博客园上看到好几个写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 }
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 try
135 {
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 else
145 {
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 else
175 {
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 finally
185 {
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#妥协进行字节序反转操作~!

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

到此结束~!

/**
*
* @author 失足程序员
* @Blog http://www.cnblogs.com/ty408/
* @mail 492794628@qq.com
* @phone 13882122019
*
*/
跪求保留标示符
本程序猿所有博客园相关全套源码奉献 淘宝SVN。特别鸣谢淘宝SVN提供免费仓储。

http://code.taobao.org/svn/flynetwork_csharp/trunk/Flynetwork/BlogTest

 
 

Java和C#的socket通信相关(转)的更多相关文章

  1. Java进阶(四十七)Socket通信

    Java进阶(四十七)Socket通信   今天讲解一个 Hello Word 级别的 Java Socket 通信的例子.具体通讯过程如下: 先启动Server端,进入一个死循环以便一直监听某端口是 ...

  2. 我看不下去鸟。。。。Java和C#的socket通信真的简单吗?

    这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...

  3. java和C#之间SOCKET通信的问题

    转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...

  4. Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制

    Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...

  5. Java实现简单的socket通信

    今天学习了一下java如何实现socket通信,感觉难点反而是在io上,因为java对socket封装已经很完善了. 今天代码花了整个晚上调试,主要原因是io的flush问题和命令行下如何运行具有pa ...

  6. Java 实现简单的 Socket 通信

    Java socket 封装了传输层的实现细节,开发人员可以基于 socket 实现应用层.本文介绍了 Java socket 简单用法. 1. 传输层协议 传输层包含了两种协议,分别是 TCP (T ...

  7. Java和C++通过Socket通信中文乱码的解决

    理想的开发状态是我开始就是C开发,一直是C的开发,现在还是C的开发,若干年后,幸运的话,我可以成为C语言的高手或者专家…… 更实际的情况是我开始是C开发,后来变成了JAVA开发,然后又做起了VC++的 ...

  8. [java]基于UDP的Socket通信Demo

    java课编程作业:在老师给的demo的基础上实现客户端发送数据到服务器端,服务器端接受客户端后进行数据广播. 整体功能类似于聊天室,代码部分不是太难,但是在本机测试的时候出现这样的问题: 服务端通过 ...

  9. Jmeter自定义编写Java代码调用socket通信

    一.前言 最近需要测试一款手机游戏的性能,找不到啥录制脚本的工具,然后,另外想办法.性能测试实际上就是对服务器的承载能力的测试,和各种类型的手机客户端没有啥多大关系,手机再好,服务器负载不了,也不能够 ...

随机推荐

  1. C++基础学习教程(八)

    转载请注明出处:http://blog.csdn.net/suool/article/details/38300117 引入 在进行下一步的学习之前,我们须要厘清几个概念. RAII 首先介绍一个编程 ...

  2. Linux------创建和终止进程

    创建进程: Linux创建两个步骤的新处理:fork()和exec().其中fork创建当前进程的能力(父进程)副本,那个孩子.父子进程只有PID不同.在这之后,该系统具有两个进程,运行相同的操作.父 ...

  3. android--自己定义ProgressDialog显示位置(其他Dialog子类都能够设置)

    1.普通情况下,系统默认的Dialog显示位置为屏幕居中: pbDialog = new ProgressDialog(MainActivity.this); pbDialog.setMessage( ...

  4. gwt CellTable中的控件按Tab键切换

    默认是 cellTable.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED); 如果要Tab,则设置为DISABLED; 并将其t ...

  5. 一步一步学android之事件篇——触摸事件

    触摸事件顾名思义就是触摸手机屏幕触发的事件,当用户触摸添加了触摸事件的View时,就是执行OnTouch()方法进行处理,下面通过一个动态获取坐标的例子来学习OnTouchListener事件,效果如 ...

  6. zoj 3822 Domination(2014牡丹江区域赛D称号)

    Domination Time Limit: 8 Seconds      Memory Limit: 131072 KB      Special Judge Edward is the headm ...

  7. html弹窗,与弹出对话框

    弹出对话框 <script type="text/JavaScript"> <!-- alert("Good Morning!"); //al ...

  8. BZOJ 2002 HNOI2010 弹飞羊 块

    标题效果,LCT解释版本:见 http://blog.csdn.net/popoqqq/article/details/38849471 如今,用一只手滑动块,并再次改写这个问题0.0 块短啊 将进入 ...

  9. android EditText的美化

    今天要做一个页面,有EditText,于是就搞起了它的美化. EditText的美化,我的第一反应是,在EditText的左边设置显示一张图片,这样会比較好看. 设置左边显示图片的属性为:androi ...

  10. Codeforces Round #257 (Div. 2) 题解

    Problem A A. Jzzhu and Children time limit per test 1 second memory limit per test 256 megabytes inp ...