c# socket 解决粘包,半包
生活随笔
收集整理的這篇文章主要介紹了
c# socket 解决粘包,半包
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
處理原理:
半包:即一條消息底層分幾次發送,先有個頭包讀取整條消息的長度,當不滿足長度時,將消息臨時緩存起來,直到滿足長度再解碼
粘包:兩條完整/不完整消息粘在一起,一般是解碼完上一條消息,然后再判斷是否有剩余字節,有的話緩存起來,循環半包處理
客戶端接收代碼:?
private void callReceived(object sender, SocketAsyncEventArgs args){var socket = sender as Socket;var bb = args.UserToken as ByteBuffer; if (args.SocketError == SocketError.Success){ bb.WriteBytes(args.Buffer, args.Offset, args.BytesTransferred);bb.MarkReaderIndex();int headLength = bb.ReadInt();int msgLength = headLength;//長度已-4 int readByteLength = bb.ReadableBytes();//解決半包if (msgLength > readByteLength){//還原讀取索引記錄 bb.ResetReaderIndex(); }else {//是否去掉包頭byte[] filthyBytes= bb.ToArray(); System.Console.WriteLine(System.Text.Encoding.UTF8.GetString(filthyBytes));//解決粘包剩余 bb.Clear();int useLength = filthyBytes.Length;int lastOffSetLength = filthyBytes.Length - useLength;if (lastOffSetLength > 0) {bb.WriteBytes(filthyBytes, lastOffSetLength, filthyBytes.Length);} }}else {//丟去byte處理System.Console.WriteLine("error callReceived");}_socket.ReceiveAsync(args);}?
服務端發送代碼:
ByteBuffer bb = ByteBuffer.Allocate(50);byte[] sendBytes=System.Text.Encoding.UTF8.GetBytes("1234567890abcdefg");Console.WriteLine("send msg length : " + sendBytes.Length);bb.WriteInt(sendBytes.Length);bb.WriteBytes(sendBytes);Send(bb.ToArray(), _clients.ToArray());public void Send(byte[] msg, params SocketAsyncEventArgs[] sockets){System.Console.WriteLine(" Send msg :");foreach (SocketAsyncEventArgs s in sockets){SocketAsyncEventArgs args = new SocketAsyncEventArgs();args.SetBuffer(msg, 0, msg.Length);//args.RemoteEndPoint = s.RemoteEndPoint;args.AcceptSocket = s.AcceptSocket;args.Completed += new EventHandler<SocketAsyncEventArgs>(this.callSended);System.Console.WriteLine(" AcceptSocket :" + s.AcceptSocket.RemoteEndPoint.ToString());args.AcceptSocket.SendAsync(args);}} ByteBuffer 類基礎跟netty相同,網上復制的using System;public class ByteBuffer {//字節緩存區private byte[] buf;//讀取索引private int readIndex = 0;//寫入索引private int writeIndex = 0;//讀取索引標記private int markReadIndex = 0;//寫入索引標記private int markWirteIndex = 0;//緩存區字節數組的長度private int capacity;/*** 構造方法*/private ByteBuffer(int capacity){buf = new byte[capacity];this.capacity = capacity;}/*** 構造方法*/private ByteBuffer(byte[] bytes){buf = bytes;this.capacity = bytes.Length;}/*** 構建一個capacity長度的字節緩存區ByteBuffer對象*/public static ByteBuffer Allocate(int capacity){return new ByteBuffer(capacity);}/*** 構建一個以bytes為字節緩存區的ByteBuffer對象,一般不推薦使用*/public static ByteBuffer Allocate(byte[] bytes){return new ByteBuffer(bytes);}/*** 翻轉字節數組,如果本地字節序列為低字節序列,則進行翻轉以轉換為高字節序列*/private byte[] flip(byte[] bytes){if (BitConverter.IsLittleEndian){Array.Reverse(bytes);}return bytes;}/*** 確定內部字節緩存數組的大小*/private int FixSizeAndReset(int currLen, int futureLen){if (futureLen > currLen){//以原大小的2次方數的兩倍確定內部字節緩存區大小int size = FixLength(currLen) * 2;if (futureLen > size){//以將來的大小的2次方的兩倍確定內部字節緩存區大小size = FixLength(futureLen) * 2;}byte[] newbuf = new byte[size];Array.Copy(buf, 0, newbuf, 0, currLen);buf = newbuf;capacity = newbuf.Length;}return futureLen;}/*** 根據length長度,確定大于此leng的最近的2次方數,如length=7,則返回值為8*/private int FixLength(int length){int n = 2;int b = 2;while (b < length){b = 2 << n;n++;}return b;}/*** 將bytes字節數組從startIndex開始的length字節寫入到此緩存區*/public ByteBuffer WriteBytes(byte[] bytes, int startIndex, int length){lock (this){int offset = length - startIndex;if (offset <= 0) return this;int total = offset + writeIndex;int len = buf.Length;FixSizeAndReset(len, total);for (int i = writeIndex, j = startIndex; i < total; i++, j++){this.buf[i] = bytes[j];}writeIndex = total;}return this;}/*** 將字節數組中從0到length的元素寫入緩存區*/public ByteBuffer WriteBytes(byte[] bytes, int length){return WriteBytes(bytes, 0, length);}/*** 將字節數組全部寫入緩存區*/public ByteBuffer WriteBytes(byte[] bytes){return WriteBytes(bytes, bytes.Length);}/*** 將一個ByteBuffer的有效字節區寫入此緩存區中*/public ByteBuffer Write(ByteBuffer buffer){if (buffer == null) return this;if (buffer.ReadableBytes() <= 0) return this;return WriteBytes(buffer.ToArray());}/*** 寫入一個int16數據*/public ByteBuffer WriteShort(short value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個uint16數據*/public ByteBuffer WriteUshort(ushort value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/**寫入字符串*/public ByteBuffer WriteString(string value){int len = value.Length;WriteInt(len);//System.Text.Encoding.BigEndianUnicode.GetBytes WriteBytes(System.Text.Encoding.UTF8.GetBytes(value));return this;}/**讀取字符串*/public String ReadString(){int len =ReadInt();byte[] bytes =new byte[len];ReadBytes(bytes,0,len); return System.Text.Encoding.UTF8.GetString(bytes);}/*** 寫入一個int32數據*/public ByteBuffer WriteInt(int value){//byte[] array = new byte[4];//for (int i = 3; i >= 0; i--)//{// array[i] = (byte)(value & 0xff);// value = value >> 8;//}//Array.Reverse(array);//Write(array);return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個uint32數據*/public ByteBuffer WriteUint(uint value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個int64數據*/public ByteBuffer WriteLong(long value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個uint64數據*/public ByteBuffer WriteUlong(ulong value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個float數據*/public ByteBuffer WriteFloat(float value){return WriteBytes(flip(BitConverter.GetBytes(value)));}/*** 寫入一個byte數據*/public ByteBuffer WriteByte(byte value){lock (this){int afterLen = writeIndex + 1;int len = buf.Length;FixSizeAndReset(len, afterLen);buf[writeIndex] = value;writeIndex = afterLen;}return this;}/*** 寫入一個double類型數據*/public ByteBuffer WriteDouble(double value) {return WriteBytes(flip(BitConverter.GetBytes(value))); }/*** 讀取一個字節*/public byte ReadByte(){byte b = buf[readIndex];readIndex++;return b;}/*** 從讀取索引位置開始讀取len長度的字節數組*/private byte[] Read(int len){byte[] bytes = new byte[len];Array.Copy(buf, readIndex, bytes, 0, len);if (BitConverter.IsLittleEndian){Array.Reverse(bytes);}readIndex += len;return bytes;}/*** 讀取一個uint16數據*/public ushort ReadUshort(){return BitConverter.ToUInt16(Read(2), 0);}/*** 讀取一個int16數據*/public short ReadShort(){return BitConverter.ToInt16(Read(2), 0);}/*** 讀取一個uint32數據*/public uint ReadUint(){return BitConverter.ToUInt32(Read(4), 0);}/*** 讀取一個int32數據*/public int ReadInt(){return BitConverter.ToInt32(Read(4), 0);}/*** 讀取一個uint64數據*/public ulong ReadUlong(){return BitConverter.ToUInt64(Read(8), 0);}/*** 讀取一個long數據*/public long ReadLong(){return BitConverter.ToInt64(Read(8), 0);}/*** 讀取一個float數據*/public float ReadFloat(){return BitConverter.ToSingle(Read(4), 0);}/*** 讀取一個double數據*/public double ReadDouble() {return BitConverter.ToDouble(Read(8), 0);}/*** 從讀取索引位置開始讀取len長度的字節到disbytes目標字節數組中* @params disstart 目標字節數組的寫入索引*/public void ReadBytes(byte[] disbytes, int disstart, int len){int size = disstart + len;for (int i = disstart; i < size; i++){disbytes[i] = this.ReadByte();}}/*** 清除已讀字節并重建緩存區*/public void DiscardReadBytes() {if(readIndex <= 0) return;int len = buf.Length - readIndex;byte[] newbuf = new byte[len];Array.Copy(buf, readIndex, newbuf, 0, len);buf = newbuf;writeIndex -= readIndex;markReadIndex -= readIndex;if (markReadIndex < 0){markReadIndex = readIndex;}markWirteIndex -= readIndex;if (markWirteIndex < 0 || markWirteIndex < readIndex || markWirteIndex < markReadIndex){markWirteIndex = writeIndex;}readIndex = 0;}/*** 清空此對象*/public void Clear(){buf = new byte[buf.Length];readIndex = 0;writeIndex = 0;markReadIndex = 0;markWirteIndex = 0;}/*** 設置開始讀取的索引*/public void SetReaderIndex(int index){if (index < 0) return;readIndex = index;}/*** 標記讀取的索引位置*/public int MarkReaderIndex(){markReadIndex = readIndex;return markReadIndex;}/*** 標記寫入的索引位置*/public void MarkWriterIndex() {markWirteIndex = writeIndex;}/*** 將讀取的索引位置重置為標記的讀取索引位置*/public void ResetReaderIndex() {readIndex = markReadIndex;}/*** 將寫入的索引位置重置為標記的寫入索引位置*/public void ResetWriterIndex() {writeIndex = markWirteIndex;}/*** 可讀的有效字節數*/public int ReadableBytes(){return writeIndex - readIndex;}/*** 獲取可讀的字節數組*/public byte[] ToArray(){byte[] bytes = new byte[writeIndex];Array.Copy(buf, 0, bytes, 0, bytes.Length);return bytes;}/*** 獲取緩存區大小*/public int GetCapacity(){return this.capacity;} }
?
最后小結一下:
相信大家都看過TCP編程的書,書本上的理論廢話長篇,說了一堆又給出一堆無用的代碼。只有看源碼才知道原理是怎樣,源碼實現一切。。。
在看的過程發現原作者寫得也不是很好,簡單的事情總是搞得那么復雜。。。。。
這行真是水深火熱啊
?
轉載于:https://www.cnblogs.com/solq/p/4315763.html
總結
以上是生活随笔為你收集整理的c# socket 解决粘包,半包的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当一个项目中同时存在webroot和we
- 下一篇: 传统节日之殇