这几天在博客园上看到好几个写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. 1 /// <summary>
  2. 2 /// 读取大端序的int
  3. 3 /// </summary>
  4. 4 /// <param name="value"></param>
  5. 5 public int ReadInt(byte[] intbytes)
  6. 6 {
  7. 7 Array.Reverse(intbytes);
  8. 8 return BitConverter.ToInt32(intbytes, 0);
  9. 9 }
  10. 10
  11. 11 /// <summary>
  12. 12 /// 写入大端序的int
  13. 13 /// </summary>
  14. 14 /// <param name="value"></param>
  15. 15 public byte[] WriterInt(int value)
  16. 16 {
  17. 17 byte[] bs = BitConverter.GetBytes(value);
  18. 18 Array.Reverse(bs);
  19. 19 return bs;
  20. 20 }

粘包问题解决。

C#代码

  1. 1 using System;
  2. 2 using System.Collections.Generic;
  3. 3 using System.IO;
  4. 4 using System.Linq;
  5. 5 using System.Text;
  6. 6 using System.Threading.Tasks;
  7. 7
  8. 8 /**
  9. 9 *
  10. 10 * @author 失足程序员
  11. 11 * @Blog http://www.cnblogs.com/ty408/
  12. 12 * @mail 492794628@qq.com
  13. 13 * @phone 13882122019
  14. 14 *
  15. 15 */
  16. 16 namespace Sz.Network.SocketPool
  17. 17 {
  18. 18 public class MarshalEndian : IMarshalEndian
  19. 19 {
  20. 20
  21. 21 public enum JavaOrNet
  22. 22 {
  23. 23 Java,
  24. 24 Net,
  25. 25 }
  26. 26
  27. 27 public MarshalEndian()
  28. 28 {
  29. 29
  30. 30 }
  31. 31
  32. 32 public static JavaOrNet JN = JavaOrNet.Net;
  33. 33
  34. 34 /// <summary>
  35. 35 /// 读取大端序的int
  36. 36 /// </summary>
  37. 37 /// <param name="value"></param>
  38. 38 public int ReadInt(byte[] intbytes)
  39. 39 {
  40. 40 Array.Reverse(intbytes);
  41. 41 return BitConverter.ToInt32(intbytes, 0);
  42. 42 }
  43. 43
  44. 44 /// <summary>
  45. 45 /// 写入大端序的int
  46. 46 /// </summary>
  47. 47 /// <param name="value"></param>
  48. 48 public byte[] WriterInt(int value)
  49. 49 {
  50. 50 byte[] bs = BitConverter.GetBytes(value);
  51. 51 Array.Reverse(bs);
  52. 52 return bs;
  53. 53 }
  54. 54
  55. 55 //用于存储剩余未解析的字节数
  56. 56 private List<byte> _LBuff = new List<byte>(2);
  57. 57
  58. 58 //字节数常量一个消息id4个字节
  59. 59 const long ConstLenght = 4L;
  60. 60
  61. 61 public void Dispose()
  62. 62 {
  63. 63 this.Dispose(true);
  64. 64 GC.SuppressFinalize(this);
  65. 65 }
  66. 66
  67. 67 protected virtual void Dispose(bool flag1)
  68. 68 {
  69. 69 if (flag1)
  70. 70 {
  71. 71 IDisposable disposable = this._LBuff as IDisposable;
  72. 72 if (disposable != null) { disposable.Dispose(); }
  73. 73 }
  74. 74 }
  75. 75
  76. 76 public byte[] Encoder(SocketMessage msg)
  77. 77 {
  78. 78 MemoryStream ms = new MemoryStream();
  79. 79 BinaryWriter bw = new BinaryWriter(ms, UTF8Encoding.Default);
  80. 80 byte[] msgBuffer = msg.MsgBuffer;
  81. 81
  82. 82 if (msgBuffer != null)
  83. 83 {
  84. 84 switch (JN)
  85. 85 {
  86. 86 case JavaOrNet.Java:
  87. 87 bw.Write(WriterInt(msgBuffer.Length + 4));
  88. 88 bw.Write(WriterInt(msg.MsgID));
  89. 89 break;
  90. 90 case JavaOrNet.Net:
  91. 91 bw.Write((Int32)(msgBuffer.Length + 4));
  92. 92 bw.Write(msg.MsgID);
  93. 93 break;
  94. 94 }
  95. 95
  96. 96 bw.Write(msgBuffer);
  97. 97 }
  98. 98 else
  99. 99 {
  100. 100 switch (JN)
  101. 101 {
  102. 102 case JavaOrNet.Java:
  103. 103 bw.Write(WriterInt(0));
  104. 104 break;
  105. 105 case JavaOrNet.Net:
  106. 106 bw.Write((Int32)0);
  107. 107 break;
  108. 108 }
  109. 109 }
  110. 110 bw.Close();
  111. 111 ms.Close();
  112. 112 bw.Dispose();
  113. 113 ms.Dispose();
  114. 114 return ms.ToArray();
  115. 115 }
  116. 116
  117. 117 public List<SocketMessage> Decoder(byte[] buff, int len)
  118. 118 {
  119. 119 //拷贝本次的有效字节
  120. 120 byte[] _b = new byte[len];
  121. 121 Array.Copy(buff, 0, _b, 0, _b.Length);
  122. 122 buff = _b;
  123. 123 if (this._LBuff.Count > 0)
  124. 124 {
  125. 125 //拷贝之前遗留的字节
  126. 126 this._LBuff.AddRange(_b);
  127. 127 buff = this._LBuff.ToArray();
  128. 128 this._LBuff.Clear();
  129. 129 this._LBuff = new List<byte>(2);
  130. 130 }
  131. 131 List<SocketMessage> list = new List<SocketMessage>();
  132. 132 MemoryStream ms = new MemoryStream(buff);
  133. 133 BinaryReader buffers = new BinaryReader(ms, UTF8Encoding.Default);
  134. 134 try
  135. 135 {
  136. 136 byte[] _buff;
  137. 137 Label_0073:
  138. 138 //判断本次解析的字节是否满足常量字节数
  139. 139 if ((buffers.BaseStream.Length - buffers.BaseStream.Position) < ConstLenght)
  140. 140 {
  141. 141 _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position));
  142. 142 this._LBuff.AddRange(_buff);
  143. 143 }
  144. 144 else
  145. 145 {
  146. 146 long offset = 0;
  147. 147 switch (JN)
  148. 148 {
  149. 149 case JavaOrNet.Java:
  150. 150 offset = ReadInt(buffers.ReadBytes(4));
  151. 151 break;
  152. 152 case JavaOrNet.Net:
  153. 153 offset = buffers.ReadInt32();
  154. 154 break;
  155. 155 }
  156. 156
  157. 157 //剩余字节数大于本次需要读取的字节数
  158. 158 if (offset <= (buffers.BaseStream.Length - buffers.BaseStream.Position))
  159. 159 {
  160. 160 int msgID = 0;
  161. 161 switch (JN)
  162. 162 {
  163. 163 case JavaOrNet.Java:
  164. 164 msgID = ReadInt(buffers.ReadBytes(4));
  165. 165 break;
  166. 166 case JavaOrNet.Net:
  167. 167 msgID = buffers.ReadInt32();
  168. 168 break;
  169. 169 }
  170. 170 _buff = buffers.ReadBytes((int)(offset - 4));
  171. 171 list.Add(new SocketMessage(msgID, _buff));
  172. 172 goto Label_0073;
  173. 173 }
  174. 174 else
  175. 175 {
  176. 176 //剩余字节数刚好小于本次读取的字节数 存起来,等待接受剩余字节数一起解析
  177. 177 buffers.BaseStream.Seek(ConstLenght, SeekOrigin.Current);
  178. 178 _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position));
  179. 179 this._LBuff.AddRange(_buff);
  180. 180 }
  181. 181 }
  182. 182 }
  183. 183 catch { }
  184. 184 finally
  185. 185 {
  186. 186 buffers.Close();
  187. 187 if (buffers != null) { buffers.Dispose(); }
  188. 188 ms.Close();
  189. 189 if (ms != null) { ms.Dispose(); }
  190. 190 }
  191. 191 return list;
  192. 192 }
  193. 193 }
  194. 194 }

java netty

  1. 1 /*
  2. 2 * To change this license header, choose License Headers in Project Properties.
  3. 3 * To change this template file, choose Tools | Templates
  4. 4 * and open the template in the editor.
  5. 5 */
  6. 6 package sz.network.socketpool.nettypool;
  7. 7
  8. 8 import io.netty.buffer.ByteBuf;
  9. 9 import io.netty.buffer.Unpooled;
  10. 10 import io.netty.channel.ChannelHandlerContext;
  11. 11 import io.netty.handler.codec.ByteToMessageDecoder;
  12. 12 import java.nio.ByteOrder;
  13. 13 import java.util.ArrayList;
  14. 14 import java.util.List;
  15. 15 import org.apache.log4j.Logger;
  16. 16
  17. 17 /**
  18. 18 * 解码器
  19. 19 */
  20. 20 class NettyDecoder extends ByteToMessageDecoder {
  21. 21
  22. 22 private static final Logger logger = Logger.getLogger(NettyDecoder.class);
  23. 23
  24. 24 private byte ZreoByteCount = 0;
  25. 25 private ByteBuf bytes;
  26. 26 private final ByteOrder endianOrder = ByteOrder.LITTLE_ENDIAN;
  27. 27 private long secondTime = 0;
  28. 28 private int reveCount = 0;
  29. 29
  30. 30 public NettyDecoder() {
  31. 31
  32. 32 }
  33. 33
  34. 34 ByteBuf bytesAction(ByteBuf inputBuf) {
  35. 35 ByteBuf bufferLen = Unpooled.buffer();
  36. 36 if (bytes != null) {
  37. 37 bufferLen.writeBytes(bytes);
  38. 38 bytes = null;
  39. 39 }
  40. 40 bufferLen.writeBytes(inputBuf);
  41. 41 return bufferLen;
  42. 42 }
  43. 43
  44. 44 /**
  45. 45 * 留存无法读取的byte等待下一次接受的数据包
  46. 46 *
  47. 47 * @param bs 数据包
  48. 48 * @param startI 起始位置
  49. 49 * @param lenI 结束位置
  50. 50 */
  51. 51 void bytesAction(ByteBuf intputBuf, int startI, int lenI) {
  52. 52 if (lenI - startI > 0) {
  53. 53 bytes = Unpooled.buffer();
  54. 54 bytes.writeBytes(intputBuf, startI, lenI);
  55. 55 }
  56. 56 }
  57. 57
  58. 58 @Override
  59. 59 protected void decode(ChannelHandlerContext chc, ByteBuf inputBuf, List<Object> outputMessage) {
  60. 60 if (System.currentTimeMillis() - secondTime < 1000L) {
  61. 61 reveCount++;
  62. 62 } else {
  63. 63 secondTime = System.currentTimeMillis();
  64. 64 reveCount = 0;
  65. 65 }
  66. 66
  67. 67 if (reveCount > 50) {
  68. 68 logger.error("发送消息过于频繁");
  69. 69 chc.disconnect();
  70. 70 return;
  71. 71 }
  72. 72
  73. 73 if (inputBuf.readableBytes() > 0) {
  74. 74 ZreoByteCount = 0;
  75. 75 //重新组装字节数组
  76. 76 ByteBuf buffercontent = bytesAction(inputBuf);
  77. 77 List<NettyMessageBean> megsList = new ArrayList<>(0);
  78. 78 for (;;) {
  79. 79 //读取 消息长度(short)和消息ID(int) 需要 8 个字节
  80. 80 if (buffercontent.readableBytes() >= 8) {
  81. 81 ///读取消息长度
  82. 82 int len = buffercontent.readInt();
  83. 83 if (buffercontent.readableBytes() >= len) {
  84. 84 int messageid = buffercontent.readInt();///读取消息ID
  85. 85 ByteBuf buf = buffercontent.readBytes(len - 4);//读取可用字节数;
  86. 86 megsList.add(new NettyMessageBean(chc, messageid, buf.array()));
  87. 87 //第二次重组
  88. 88 if (buffercontent.readableBytes() > 0) {
  89. 89 bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes());
  90. 90 buffercontent = Unpooled.buffer();
  91. 91 buffercontent.writeBytes(bytes);
  92. 92 continue;
  93. 93 } else {
  94. 94 break;
  95. 95 }
  96. 96 }
  97. 97 ///重新设置读取进度
  98. 98 buffercontent.setIndex(buffercontent.readableBytes() - 2, inputBuf.readableBytes());
  99. 99 }
  100. 100 ///缓存预留的字节
  101. 101 bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes());
  102. 102 break;
  103. 103 }
  104. 104 outputMessage.addAll(megsList);
  105. 105 } else {
  106. 106 ZreoByteCount++;
  107. 107 if (ZreoByteCount >= 3) {
  108. 108 //todo 空包处理 考虑连续三次空包,断开链接
  109. 109 logger.error("decode 空包处理 连续三次空包");
  110. 110 chc.close();
  111. 111 }
  112. 112 }
  113. 113 }
  114. 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. Python学习入门基础教程(learning Python)--2.3.1 Python传参函数设计

    本节主要讨论设计传递多个参数子函数的设计方法. 在2.3节里我们讨论了如何自己设计一个带参数的子函数的设计方法,现在我们研究一下如何传递两个及以上参数的设计方法. 函数为何要带参数呢?其实原因很简单, ...

  2. java.lang.NullPointerException错误分析

    java.lang.NullPointerException是什么错误 你使用了空的指针.在java中虽然号称抛弃了C++中不安全的指针,但其实他所有的东西你都可以理解为指针.这种情况一般发生在你使用 ...

  3. windows phone (15) UI变换上

    原文:windows phone (15) UI变换上 在wp中只要是继承自UIElement 的任何对象都可以应用变换,当然包含Textblock,Rectangle等所有的元素,下面我们使用Tex ...

  4. 高速幂 POW优化

    #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h&g ...

  5. Coco2dx-3.0中怎样调用LUA

    一个用3.0的工具导出类到lua,自己主动生成代码的方法. 曾经要导出c++类到lua.就得手动维护pkg文件,那简直就是噩梦.3.0以后就会感觉生活非常轻松了. 以下我就在说下详细做法. 1.安装必 ...

  6. 领域驱动设计(DDD)部分核心概念的个人理解(转)

    领域驱动设计(DDD)是一种基于模型驱动的软件设计方式.它以领域为核心,分析领域中的问题,通过建立一个领域模型来有效的解决领域中的核心的复杂问题.Eric Ivans为领域驱动设计提出了大量的最佳实践 ...

  7. Qt计算器开发(二):信号槽实现数学表达式合法性检查

    表达式的合法性 由于我们的计算器不是单步计算的,所以我们能够一次性输入一个长表达式.然而假设用户输入的长表达式不合法的话,那么就会引发灾难.所以有必要对于用户的输入做一个限制. 一些限制举例: 比方, ...

  8. Jenkins(转)

    1  修改jenkins的根目录,默认地在C:\Documents and Settings\AAA\.jenkins . .jenkins ├─jobs│  └─JavaHelloWorld│    ...

  9. 【Python项目】配合爱漫画爬取漫画脚本而设计的GUI漫画阅读器 (一)

    博客园的第一个坑,想想都有点小激动 =3= 首先是那个爬虫的地址: [原创]最近写的一个比较hack的小爬虫 选择工具: 以前用过Qt,那么选pyqt4也就是情理之中了. 明确需求: 0.首先,要读取 ...

  10. hdu 4308 Saving Princess claire_ BFS

    为了准备算法考试刷的,想明确一点即可,全部的传送门相当于一个点,当遇到一个传送门的时候,把全部的传送门都压入队列进行搜索 贴代码: #include <iostream> #include ...