这几天在博客园上看到好几个写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. shell加法

    echo 1597+1469+1468+2591+1260+1068+1019+993|bc http://bbs.chinaunix.net/thread-161085-1-1.html http: ...

  2. 【OC加强】NSDate的使用方法——日期时间在实际开发中比較有用

    (1)日期的最主要知识点就是日期转换成字符串格式化输出,相反就是依照某个格式把字符串转换成日期. (2)一般关于时区的设置非常少用到,仅仅要了解就可以. #import <Foundation/ ...

  3. mysql安装注意

    mysql安装教程,网上到处都有,我这里就不细说了. 但是有一点要注意,安装完之后,点击MySql 5.5 Command Line Client时,有可能出现一闪而过,打不开mysql的情况: 首先 ...

  4. centos6.5安装nodejs

    Preface(前言) 一次偶然的机会知道有nodejs这个东西,确实对它还是非常感兴趣的.刚開始仅仅知道它能让javascript写后台,然后前后台都由javascript来写,确实认为真的挺爽,毕 ...

  5. 【Android接口实现】ActionBar利用整理的一些细节

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 关于ActionBar的使用,非常多大神早就已经写了非常好的文章进行了介绍,所以ActionBar的基本使用 ...

  6. WPF命令參数CommandParameter

    XAML代码例如以下: <Window x:Class="Demo006.MainWindow" xmlns="http://schemas.microsoft.c ...

  7. 如何判断一个Http Message的结束——python源码解读

    HTTP/1.1 默认的连接方式是长连接,不能通过简单的TCP连接关闭判断HttpMessage的结束. 以下是几种判断HttpMessage结束的方式: 1.      HTTP协议约定status ...

  8. 使用Spring的@Autowired 实现DAO, Service, Controller三层的注入(转)

    简述: 结合Spring和Hibernate进行开发 使用@Autowired实现依赖注入, 实现一个学生注册的功能,做一个技术原型 从DAO(Repository) -> Service -& ...

  9. TMS320F28335项目开发记录2_CCS与JTAG仿真器连接问题汇总

    CCS与仿真器连接问题 实际使用过程中.仿真器和CCS连接可能出现这样或那样的问题,或许你的连接非常成功,没碰到过什么问题.但我的问题的确不少,可能与电脑配置有关吧,也可能与人品有关吧. 以下的自己的 ...

  10. Codeforces Round #256 (Div. 2) A. Rewards

    A. Rewards time limit per test 1 second memory limit per test 256 megabytes input standard input out ...