程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 我看不下去鳥。。。。Java和C#的socket通信真的簡單嗎?,

我看不下去鳥。。。。Java和C#的socket通信真的簡單嗎?,

編輯:C#入門知識

我看不下去鳥。。。。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#妥協進行字節序反轉操作~!

就不在添加調試和測試代碼和結果因為覺得沒多少意義~!

到此結束~!

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved