Java和C#的socket通信相关(转)

这几天在博客园上看到好几个写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 [email protected]
 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#妥协进行字节序反转操作~!

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

到此结束~!

时间: 2024-11-05 16:30:56

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

Java网络编程之Socket通信(二)

之前在前面已经介绍了Socket通信的一些基本原理,以及如何让客户端与服务器端建立通信,和实现通信的一些基本步骤(包括首先使得服务器端与客户端建立连接,建立连接之后,服务器端开始侦听客户端的请求,侦听到客户端的请求之后,通过输入输出流处理相关信息实现通信,最后通信完毕结束通信等一系列流程). 但是之前只是单个客户端与服务器进行通信,而我们实际应用中单个客户端的情况几乎不存在,都是多个客户端同时与服务器进行交互(这里同时交互就会出现并发性的问题,对于并发性的问题暂时还不是很懂,只知道有这个概念),

Java 和 Python 的 Socket 通信

网络上两个程序通过一个双向通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket支持的协议有多种,这里主要介绍基于 TCP/IP 协议族的 Socket 编程. 首先,IP协议族决定了socket的地址类型,在通信中必须采用对应的地址.AF_INET(AF 表示 Adress Family)表示要用 ipv4 地址(32位)与端口号(16位)的组合. 然后,根据传输协议又分为:流式 Socket(SOCK_STREAM) 和数据报式 Socket(SOCK_DGRAM):

[java]基于UDP的Socket通信Demo

java课编程作业:在老师给的demo的基础上实现客户端发送数据到服务器端,服务器端接受客户端后进行数据广播. 整体功能类似于聊天室,代码部分不是太难,但是在本机测试的时候出现这样的问题: 服务端通过将每一个Socket客户端的IP存入Set集合,每次接受到数据后都向当前所有的IP转发.但是本机演示的时候所有开的ChatClient客户端都是同一IP,怎么测试呢? 解决办法就是本机测试时候服务端向多个不同的端口转发就好了,这样跑起来的客户端是在不同端口上进行监听的(只是为了实现广播,实际应用下还

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

这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己开发的一套组件. Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty.游戏行业使用也是居多. 关于socket的底层写法,实在太多,我就不在BB. 这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序.而java使用的是大端序作为字节序. 也就是说比如一个

Java实现简单的socket通信

今天学习了一下java如何实现socket通信,感觉难点反而是在io上,因为java对socket封装已经很完善了. 今天代码花了整个晚上调试,主要原因是io的flush问题和命令行下如何运行具有package的类,不过最后问题基本都解决了,把代码贴出来供大家参考 server public class TcpServer { public static void main(String[] args) throws Exception { ServerSocket server = new S

[工作小记]JAVA与C#的Socket通信

在日常的开发中,不同的传感器支持的开发语言常常是不同的.例如Kinect一般是用C++开发的,Leapmotion用JAVA开发比较多. 去年暑假(17年暑假)做的一个项目是:用Leapmotion捕捉人手的五个手指关节信息,然后经过变换,传输给一个同样具有五个手指的机械手. 一开始拿到的开发框架,Leapmotion是用JAVA开发的:机械手AR10是用C#开发. 于是分别通过编程实现,使得JAVA端能够使Leapmotion捕捉人手五个手指10个关节的信息并保存为文本文件: C#端能够使AR

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

理想的开发状态是我开始就是C开发,一直是C的开发,现在还是C的开发,若干年后,幸运的话,我可以成为C语言的高手或者专家…… 更实际的情况是我开始是C开发,后来变成了JAVA开发,然后又做起了VC++的开发,几年下来,发现自己几乎一无所长,成为了编程界的万金油,这种产物很重要,但是也比较尴尬. 前一段时间我用C开发了一个服务器程序,前端是VC开发的客户端. 现在我用JAVA开发了一个类似的服务器程序,前端依然是VC开发的客户端,运行时发现,中文乱码了. 先说说我的问题场景,很常见: 1 VC++客

基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础. 下面看看,Java实现的一个服务端和客户端通信的例子. NIO模式的基本原理描述如下: 服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSock

java 和 C++ Socket通信(java作为服务端server,C++作为客户端client,解决中文乱码问题GBK和UTF8)

原文链接: http://www.cnblogs.com/kenkofox/archive/2010/04/25/1719649.html 代码: http://files.cnblogs.com/kenkofox/Client-CPlusPlus.rarhttp://files.cnblogs.com/kenkofox/Server_Java.rar java和C++使用Socket通信,其实底层Socket都是相通的,所以只需要按照各自的语法去做就是了. java服务器端使用ServerSo