这几天在博客园上看到好几个写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. 让Linux开机运行命令

    开机的时候需要linux 自动执行命令很简单 只需要把要执行的命令输入操作系统启动的时候要加载的文件里面就行了,一般写在 /etc/rc.local里面 #vim /etc/rc.local 按o键  ...

  2. Mac OS X在建筑Python科学计算环境

    经验(比如这篇日志:http://blog.csdn.net/waleking/article/details/7578517).他们推荐使用Mac Ports这种软件来管理和安装全部的安装包.依照这 ...

  3. 异常学习笔记+打包+doc该软件包编译

    jvm调用默认的异常处理机制printStackTrace办法      欲了解更多异常处理.问题      捕获异常代码块出现继承关系 应该把被继承的异常放在子类异常块的后面 watermark/2 ...

  4. c#开发微信公众平台

    之前帮公司开发过微信公众账号,今天特别将过程再回顾记录下来. 1.URL配置 启用开发模式需要先成为开发者,而且编辑模式和开发模式只能选择一个,进入微信公众平台-开发模式,如下: 从上面可以看出,点击 ...

  5. TextBox自定义Mac输入框类

    using System.Windows.Controls; namespace test { public class MacTextBox : TextBox { private string _ ...

  6. 恢复js文件在windows默认打开方式

    解决办法: 运行 regedit 打开注册表编辑器,定位 "HKEY_CLASSES_ROOT" > ".js" 这一项,双击默认值将数值数据改为&quo ...

  7. [Unity3D]Unity3D游戏开发之飞机大战项目解说

    大家好,我是秦元培,欢迎大家继续关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei. 首先感谢大家对我博客的关注,今天我想和大家分享的是一个飞机大战的项目.这是一个比較综合的 ...

  8. Codeforces Round#201(div1) D. Lucky Common Subsequence

    题意:给定两个串,求出两个串的最长公共子序列,要求该公共子序列不包含virus串. 用dp+kmp实现 dp[i][j][k]表示以i结尾的字符串和以j结尾的字符串的公共子序列的长度(其中k表示该公共 ...

  9. Android实战技术:IPC方式简介教程

    非实时,通知性的方式 第一种方式就是Intent,Intent可以非常方便的通讯,但是它是非实时的,无法进行实时的像函数调用那样的实时的通讯. 实时的函数调用 但是IPC的根本目的还是为了实现函数的调 ...

  10. leetcode先刷_Remove Duplicates from Sorted List II

    删除重复节点列表中的.假设所有val如果仅仅是为了保持一个非常easy.应承担重复val节点被删除话.要保持pre节点.每当你想保存这pre问题节点,应该head节点可以被取出,好了,没问题边境控制. ...