c# java socketn 字节流_socket传输protobuf字节流的实例详解,

1 using System; 2 using System.Collections.Generic; 3 4 /// 5 /// 字节缓冲处理类,本类仅处理大字节序 6 /// 警告,本类非线程安全 7 /// 8 public class ByteBuffer 9 { 10 //字节缓存区 11 private byte[] buf; 12 //读取索引 13 private int readIndex = 0; 14 //写入索引 15 private int writeIndex = 0; 16 //读取索引标记 17 private int markReadIndex = 0; 18 //写入索引标记 19 private int markWirteIndex = 0; 20 //缓存区字节数组的长度 21 private int capacity; 22 23 //对象池 24 private static List pool = new List(); 25 private static int poolMaxCount = 200; 26 //此对象是否池化 27 private bool isPool = false; 28 29 /// 30 /// 构造方法 31 /// 32 /// 初始容量 33 private ByteBuffer(int capacity) 34 { 35 buf = new byte[capacity]; 36 this.capacity = capacity; 37 } 38 39 /// 40 /// 构造方法 41 /// 42 /// 初始字节数组 43 private ByteBuffer(byte[] bytes) 44 { 45 buf = bytes; 46 this.capacity = bytes.Length; 47 this.readIndex = 0; 48 this.writeIndex = bytes.Length + 1; 49 } 50 51 /// 52 /// 构建一个capacity长度的字节缓存区ByteBuffer对象 53 /// 54 /// 初始容量 55 /// ByteBuffer对象 56 public static ByteBuffer Allocate(int capacity) 57 { 58 return new ByteBuffer(capacity); 59 } 60 61 /// 62 /// 构建一个以bytes为字节缓存区的ByteBuffer对象,一般不推荐使用 63 /// 64 /// 初始字节数组 65 /// ByteBuffer对象 66 public static ByteBuffer Allocate(byte[] bytes) 67 { 68 return new ByteBuffer(bytes); 69 } 70 71 /// 72 /// 获取一个池化的ByteBuffer对象,池化的对象必须在调用Dispose后才会推入池中,否则此方法等同于Allocate(int capacity)方法,此方法为线程安全的 73 /// 74 /// ByteBuffer对象的初始容量大小,如果缓存池中没有对象,则对象的容量大小为此值,否则为池中对象的实际容量值 75 /// 76 public static ByteBuffer GetFromPool(int capacity) 77 { 78 lock (pool) 79 { 80 ByteBuffer bbuf; 81 if (pool.Count == 0) 82 { 83 bbuf = Allocate(capacity); 84 bbuf.isPool = true; 85 return bbuf; 86 } 87 int lastIndex = pool.Count - 1; 88 bbuf = pool[lastIndex]; 89 pool.RemoveAt(lastIndex); 90 if (!bbuf.isPool) 91 { 92 bbuf.isPool = true; 93 } 94 return bbuf; 95 } 96 } 97 98 /// 99 /// 根据length长度,确定大于此leng的最近的2次方数,如length=7,则返回值为8100 /// 101 /// 参考容量102 /// 比参考容量大的最接近的2次方数103 private int FixLength(int length)104 {105 int n = 2;106 int b = 2;107 while (b < length)108 {109 b = 2 << n;110 n++;111 }112 return b;113 }114 115 /// 116 /// 翻转字节数组,如果本地字节序列为低字节序列,则进行翻转以转换为高字节序列117 /// 118 /// 待转为高字节序的字节数组119 /// 高字节序列的字节数组120 private byte[] flip(byte[] bytes)121 {122 if (BitConverter.IsLittleEndian)123 {124 Array.Reverse(bytes);125 }126 return bytes;127 }128 129 /// 130 /// 确定内部字节缓存数组的大小131 /// 132 /// 当前容量133 /// 将来的容量134 /// 将来的容量135 private int FixSizeAndReset(int currLen, int futureLen)136 {137 if (futureLen > currLen)138 {139 //以原大小的2次方数的两倍确定内部字节缓存区大小140 int size = FixLength(currLen) * 2;141 if (futureLen > size)142 {143 //以将来的大小的2次方的两倍确定内部字节缓存区大小144 size = FixLength(futureLen) * 2;145 }146 byte[] newbuf = new byte[size];147 Array.Copy(buf, 0, newbuf, 0, currLen);148 buf = newbuf;149 capacity = newbuf.Length;150 }151 return futureLen;152 }153 154 /// 155 /// 将bytes字节数组从startIndex开始的length字节写入到此缓存区156 /// 157 /// 待写入的字节数据158 /// 写入的开始位置159 /// 写入的长度160 public void WriteBytes(byte[] bytes, int startIndex, int length)161 {162 int offset = length - startIndex;163 if (offset <= 0) return;164 int total = offset + writeIndex;165 int len = buf.Length;166 FixSizeAndReset(len, total);167 for (int i = writeIndex, j = startIndex; i < total; i++, j++)168 {169 buf[i] = bytes[j];170 }171 writeIndex = total;172 }173 174 /// 175 /// 将字节数组中从0到length的元素写入缓存区176 /// 177 /// 待写入的字节数据178 /// 写入的长度179 public void WriteBytes(byte[] bytes, int length)180 {181 WriteBytes(bytes, 0, length);182 }183 184 /// 185 /// 将字节数组全部写入缓存区186 /// 187 /// 待写入的字节数据188 public void WriteBytes(byte[] bytes)189 {190 WriteBytes(bytes, bytes.Length);191 }192 193 /// 194 /// 将一个ByteBuffer的有效字节区写入此缓存区中195 /// 196 /// 待写入的字节缓存区197 public void Write(ByteBuffer buffer)198 {199 if (buffer == null) return;200 if (buffer.ReadableBytes() <= 0) return;201 WriteBytes(buffer.ToArray());202 }203 204 /// 205 /// 写入一个int16数据206 /// 207 /// short数据208 public void WriteShort(short value)209 {210 WriteBytes(flip(BitConverter.GetBytes(value)));211 }212 213 /// 214 /// 写入一个ushort数据215 /// 216 /// ushort数据217 public void WriteUshort(ushort value)218 {219 WriteBytes(flip(BitConverter.GetBytes(value)));220 }221 222 /// 223 /// 写入一个int32数据224 /// 225 /// int数据226 public void WriteInt(int value)227 {228 //byte[] array = new byte[4];229 //for (int i = 3; i >= 0; i--)230 //{231 // array[i] = (byte)(value & 0xff);232 // value = value >> 8;233 //}234 //Array.Reverse(array);235 //Write(array);236 WriteBytes(flip(BitConverter.GetBytes(value)));237 }238 239 /// 240 /// 写入一个uint32数据241 /// 242 /// uint数据243 public void WriteUint(uint value)244 {245 WriteBytes(flip(BitConverter.GetBytes(value)));246 }247 248 /// 249 /// 写入一个int64数据250 /// 251 /// long数据252 public void WriteLong(long value)253 {254 WriteBytes(flip(BitConverter.GetBytes(value)));255 }256 257 /// 258 /// 写入一个uint64数据259 /// 260 /// ulong数据261 public void WriteUlong(ulong value)262 {263 WriteBytes(flip(BitConverter.GetBytes(value)));264 }265 266 /// 267 /// 写入一个float数据268 /// 269 /// float数据270 public void WriteFloat(float value)271 {272 WriteBytes(flip(BitConverter.GetBytes(value)));273 }274 275 /// 276 /// 写入一个byte数据277 /// 278 /// byte数据279 public void WriteByte(byte value)280 {281 int afterLen = writeIndex + 1;282 int len = buf.Length;283 FixSizeAndReset(len, afterLen);284 buf[writeIndex] = value;285 writeIndex = afterLen;286 }287 288 /// 289 /// 写入一个byte数据290 /// 291 /// byte数据292 public void WriteByte(int value)293 {294 byte b = (byte)value;295 WriteByte(b);296 }297 298 /// 299 /// 写入一个double类型数据300 /// 301 /// double数据302 public void WriteDouble(double value)303 {304 WriteBytes(flip(BitConverter.GetBytes(value)));305 }306 307 /// 308 /// 写入一个字符309 /// 310 /// 311 public void WriteChar(char value)312 {313 WriteBytes(flip(BitConverter.GetBytes(value)));314 }315 316 /// 317 /// 写入一个布尔型数据318 /// 319 /// 320 public void WriteBoolean(bool value)321 {322 WriteBytes(flip(BitConverter.GetBytes(value)));323 }324 325 /// 326 /// 读取一个字节327 /// 328 /// 字节数据329 public byte ReadByte()330 {331 byte b = buf[readIndex];332 readIndex++;333 return b;334 }335 336 /// 337 /// 读取一个字节并转为int类型的数据338 /// 339 /// int数据340 public int ReadByteToInt()341 {342 byte b = ReadByte();343 return (int)b;344 }345 346 /// 347 /// 获取从index索引处开始len长度的字节348 /// 349 /// 350 /// 351 /// 352 private byte[] Get(int index, int len)353 {354 byte[] bytes = new byte[len];355 Array.Copy(buf, index, bytes, 0, len);356 return flip(bytes);357 }358 359 /// 360 /// 从读取索引位置开始读取len长度的字节数组361 /// 362 /// 待读取的字节长度363 /// 字节数组364 private byte[] Read(int len)365 {366 byte[] bytes = Get(readIndex, len);367 readIndex += len;368 return bytes;369 }370 371 /// 372 /// 读取一个uint16数据373 /// 374 /// ushort数据375 public ushort ReadUshort()376 {377 return BitConverter.ToUInt16(Read(2), 0);378 }379 380 /// 381 /// 读取一个int16数据382 /// 383 /// short数据384 public short ReadShort()385 {386 return BitConverter.ToInt16(Read(2), 0);387 }388 389 /// 390 /// 读取一个uint32数据391 /// 392 /// uint数据393 public uint ReadUint()394 {395 return BitConverter.ToUInt32(Read(4), 0);396 }397 398 /// 399 /// 读取一个int32数据400 /// 401 /// int数据402 public int ReadInt()403 {404 return BitConverter.ToInt32(Read(4), 0);405 }406 407 /// 408 /// 读取一个uint64数据409 /// 410 /// ulong数据411 public ulong ReadUlong()412 {413 return BitConverter.ToUInt64(Read(8), 0);414 }415 416 /// 417 /// 读取一个long数据418 /// 419 /// long数据420 public long ReadLong()421 {422 return BitConverter.ToInt64(Read(8), 0);423 }424 425 /// 426 /// 读取一个float数据427 /// 428 /// float数据429 public float ReadFloat()430 {431 return BitConverter.ToSingle(Read(4), 0);432 }433 434 /// 435 /// 读取一个double数据436 /// 437 /// double数据438 public double ReadDouble()439 {440 return BitConverter.ToDouble(Read(8), 0);441 }442 443 /// 444 /// 读取一个字符445 /// 446 /// 447 public char ReadChar()448 {449 return BitConverter.ToChar(Read(2), 0);450 }451 452 /// 453 /// 读取布尔型数据454 /// 455 /// 456 public bool ReadBoolean()457 {458 return BitConverter.ToBoolean(Read(1), 0);459 }460 461 /// 462 /// 从读取索引位置开始读取len长度的字节到disbytes目标字节数组中463 /// 464 /// 读取的字节将存入此字节数组465 /// 目标字节数组的写入索引466 /// 读取的长度467 public void ReadBytes(byte[] disbytes, int disstart, int len)468 {469 int size = disstart + len;470 for (int i = disstart; i < size; i++)471 {472 disbytes[i] = this.ReadByte();473 }474 }475 476 /// 477 /// 获取一个字节478 /// 479 /// 480 /// 481 public byte GetByte(int index)482 {483 return buf[index];484 }485 486 /// 487 /// 获取全部字节488 /// 489 /// 490 public byte[] GetBytes()491 {492 return buf;493 }494 495 /// 496 /// 获取一个双精度浮点数据,不改变数据内容497 /// 498 /// 字节索引499 /// 500 public double GetDouble(int index)501 {502 return BitConverter.ToDouble(Get(0, 8), 0);503 }504 505 /// 506 /// 获取一个浮点数据,不改变数据内容507 /// 508 /// 字节索引509 /// 510 public float GetFloat(int index)511 {512 return BitConverter.ToSingle(Get(0, 4), 0);513 }514 515 /// 516 /// 获取一个长整形数据,不改变数据内容517 /// 518 /// 字节索引519 /// 520 public long GetLong(int index)521 {522 return BitConverter.ToInt64(Get(0, 8), 0);523 }524 525 /// 526 /// 获取一个整形数据,不改变数据内容527 /// 528 /// 字节索引529 /// 530 public int GetInt(int index)531 {532 return BitConverter.ToInt32(Get(0, 4), 0);533 }534 535 /// 536 /// 获取一个短整形数据,不改变数据内容537 /// 538 /// 字节索引539 /// 540 public int GetShort(int index)541 {542 return BitConverter.ToInt16(Get(0, 2), 0);543 }544 545 546 /// 547 /// 清除已读字节并重建缓存区548 /// 549 public void DiscardReadBytes()550 {551 if (readIndex <= 0) return;552 int len = buf.Length - readIndex;553 byte[] newbuf = new byte[len];554 Array.Copy(buf, readIndex, newbuf, 0, len);555 buf = newbuf;556 writeIndex -= readIndex;557 markReadIndex -= readIndex;558 if (markReadIndex < 0)559 {560 markReadIndex = readIndex;561 }562 markWirteIndex -= readIndex;563 if (markWirteIndex < 0 || markWirteIndex < readIndex || markWirteIndex < markReadIndex)564 {565 markWirteIndex = writeIndex;566 }567 readIndex = 0;568 }569 570 /// 571 /// 清空此对象,但保留字节缓存数组(空数组)572 /// 573 public void Clear()574 {575 buf = new byte[buf.Length];576 readIndex = 0;577 writeIndex = 0;578 markReadIndex = 0;579 markWirteIndex = 0;580 capacity = buf.Length;581 }582 583 /// 584 /// 释放对象,清除字节缓存数组,如果此对象为可池化,那么调用此方法将会把此对象推入到池中等待下次调用585 /// 586 public void Dispose()587 {588 readIndex = 0;589 writeIndex = 0;590 markReadIndex = 0;591 markWirteIndex = 0;592 if (isPool)593 {594 lock (pool)595 {596 if (pool.Count < poolMaxCount)597 {598 pool.Add(this);599 }600 }601 }602 else603 {604 capacity = 0;605 buf = null;606 }607 }608 609 /// 610 /// 设置/获取读指针位置611 /// 612 public int ReaderIndex613 {614 get615 {616 return readIndex;617 }618 set619 {620 if (value < 0) return;621 readIndex = value;622 }623 }624 625 /// 626 /// 设置/获取写指针位置627 /// 628 public int WriterIndex629 {630 get631 {632 return writeIndex;633 }634 set635 {636 if (value < 0) return;637 writeIndex = value;638 }639 }640 641 /// 642 /// 标记读取的索引位置643 /// 644 public void MarkReaderIndex()645 {646 markReadIndex = readIndex;647 }648 649 /// 650 /// 标记写入的索引位置651 /// 652 public void MarkWriterIndex()653 {654 markWirteIndex = writeIndex;655 }656 657 /// 658 /// 将读取的索引位置重置为标记的读取索引位置659 /// 660 public void ResetReaderIndex()661 {662 readIndex = markReadIndex;663 }664 665 /// 666 /// 将写入的索引位置重置为标记的写入索引位置667 /// 668 public void ResetWriterIndex()669 {670 writeIndex = markWirteIndex;671 }672 673 /// 674 /// 可读的有效字节数675 /// 676 /// 可读的字节数677 public int ReadableBytes()678 {679 return writeIndex - readIndex;680 }681 682 /// 683 /// 获取可读的字节数组684 /// 685 /// 字节数据686 public byte[] ToArray()687 {688 byte[] bytes = new byte[writeIndex];689 Array.Copy(buf, 0, bytes, 0, bytes.Length);690 return bytes;691 }692 693 /// 694 /// 获取缓存区容量大小695 /// 696 /// 缓存区容量697 public int GetCapacity()698 {699 return this.capacity;700 }701 702 /// 703 /// 简单的数据类型704 /// 705 public enum LengthType706 {707 //byte类型708 BYTE,709 //short类型710 SHORT,711 //int类型712 INT713 }714 715 /// 716 /// 写入一个数据717 /// 718 /// 待写入的数据719 /// 待写入的数据类型720 public void WriteValue(int value, LengthType type)721 {722 switch (type)723 {724 case LengthType.BYTE:725 this.WriteByte(value);726 break;727 case LengthType.SHORT:728 this.WriteShort((short)value);729 break;730 default:731 this.WriteInt(value);732 break;733 }734 }735 736 /// 737 /// 读取一个值,值类型根据type决定,int或short或byte738 /// 739 /// 值类型740 /// int数据741 public int ReadValue(LengthType type)742 {743 switch (type)744 {745 case LengthType.BYTE:746 return ReadByteToInt();747 case LengthType.SHORT:748 return (int)ReadShort();749 default:750 return ReadInt();751 }752 }753 754 /// 755 /// 写入一个字符串756 /// 757 /// 待写入的字符串758 /// 写入的字符串长度类型759 public void WriteUTF8String(string content, LengthType lenType)760 {761 byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes(content);762 int max;763 if (lenType == LengthType.BYTE)764 {765 WriteByte(bytes.Length);766 max = byte.MaxValue;767 }768 else if (lenType == LengthType.SHORT)769 {770 WriteShort((short)bytes.Length);771 max = short.MaxValue;772 }773 else774 {775 WriteInt(bytes.Length);776 max = int.MaxValue;777 }778 if (bytes.Length > max)779 {780 WriteBytes(bytes, 0, max);781 }782 else783 {784 WriteBytes(bytes, 0, bytes.Length);785 }786 }787 788 /// 789 /// 读取一个字符串790 /// 791 /// 需读取的字符串长度792 /// 字符串793 public string ReadUTF8String(int len)794 {795 byte[] bytes = new byte[len];796 this.ReadBytes(bytes, 0, len);797 return System.Text.UTF8Encoding.UTF8.GetString(bytes);798 }799 800 /// 801 /// 读取一个字符串802 /// 803 /// 字符串长度类型804 /// 字符串805 public string ReadUTF8String(LengthType lenType)806 {807 int len = ReadValue(lenType);808 return ReadUTF8String(len);809 }810 811 /// 812 /// 复制一个对象,具有与原对象相同的数据,不改变原对象的数据813 /// 814 /// 815 public ByteBuffer Copy()816 {817 return Copy(0);818 }819 820 public ByteBuffer Copy(int startIndex)821 {822 if (buf == null)823 {824 return new ByteBuffer(16);825 }826 byte[] target = new byte[buf.Length - startIndex];827 Array.Copy(buf, startIndex, target, 0, target.Length);828 ByteBuffer buffer = new ByteBuffer(target.Length);829 buffer.WriteBytes(target);830 return buffer;831 }832 }View Code

当然,c#中虽然没有ByteBuffer,但也有拼接字节数组的方法,比如Send( [] bytes = [data.Length + [] length = BitConverter.GetBytes( Array.Copy(length, , bytes, , Array.Copy(data, , bytes, mSocket.Send(bytes);

}

字节数组拼接好后,就可以使用socket的send方法发送了,不过这一篇先继续讲完接收数据的处理

接收数据的顺序是先接收消息长度,然后根据消息长度接收指定长度的消息1 void ReceiveMessage() 2 { 3 //上文说过,一个完整的消息是 消息长度+消息内容 4 //所以先创建一个长度4的字节数组,用于接收消息长度 5 byte[] recvBytesHead = GetBytesReceive(4); 6 //将消息长度字节组转为int数值 7 int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, 0)); 8 //根据消息长度接收指定长度的字节组,这个字节组就是完整的消息内容 9 byte[] recvBytesBody = GetBytesReceive(bodyLength);10 //最后反序列化消息的内容11 Test message = ProtobufSerilizer.DeSerialize(messageBody);12 }

GetBytesRecive方法用于接收消息,并解决粘包、少包的问题,代码如下1 /// 2 /// 接收数据并处理 3 /// 4 /// 5 /// 6 byte[] GetBytesReceive(int length) 7 { 8 //创建指定长度的字节组 9 byte[] recvBytes = new byte[length];10 //设置每次接收包的最大长度为1024个字节11 int packageMaxLength = 1024;12 //使用循环来保证接收的数据是完整的,如果剩余长度大于0,证明接收未完成13 while (length > 0)14 {15 //创建字节组,用于存放需要接收的字节流16 byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength];17 int iBytesBody = 0;18 //根据剩余需接收的长度来设置接收数据的长度19 if (length >= receiveBytes.Length)20 iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, 0);21 else22 iBytesBody = mSocket.Receive(receiveBytes, length, 0);23 receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);24 //减去已接收的长度25 length -= iBytesBody;26 }27 return recvBytes;28 }

到这里,消息的简单发送和接收就基本搞定了,但是,实际项目中,我们的消息数量肯定不会只有一条,如果是长链接的项目,更是需要一直接收和发送消息,该怎么办?

众所周知,unity的UI上的显示只能在主线程中执行,可是如果我们在主线程一直接收和发送消息,那体验将会极差,所以我们必须另外开启线程来负责消息的接收和发送,下一篇就是使用多线程来完成socket通讯

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 C#Java 实现 Socket 传输 Protobuf 字节的过程可以分为以下几个步骤: 1. 定义 Protobuf 消息结构 首先,需要使用 Protocol Buffers 定义消息结构。假设我们要传输的消息结构如下: ``` message Person { string name = 1; int32 age = 2; repeated string phone_number = 3; } ``` 2. 生成代码 使用 protobuf 编译器生成 C#Java 的代码,方法如下: ``` protoc --csharp_out=. person.proto protoc --java_out=. person.proto ``` 3. C# 实现 Socket 发送 首先,在 C# 中创建一个 `Person` 对象,并将其序列化为字节数组,然后将其发送到 Java 服务器: ```csharp using System; using System.Net.Sockets; using Google.Protobuf; class Program { static void Main(string[] args) { // 创建 Person 对象 var person = new Person { Name = "张三", Age = 18, PhoneNumber = { "123456789", "987654321" } }; // 将 Person 对象序列化为字节数组 byte[] data = person.ToByteArray(); // 创建 Socket 连接 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect("127.0.0.1", 8888); // 发送数据 socket.Send(data); // 关闭连接 socket.Shutdown(SocketShutdown.Both); socket.Close(); } } ``` 4. Java 实现 Socket 接收 在 Java 中,我们需要创建一个 `ServerSocket`,并监听指定的端口。当有连接请求时,我们可以使用 `Socket` 接收数据,并将其反序列化为 `Person` 对象: ```java import com.example.PersonOuterClass.Person; import com.google.protobuf.InvalidProtocolBufferException; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Main { public static void main(String[] args) throws IOException { // 创建 ServerSocket,监听指定端口 ServerSocket serverSocket = new ServerSocket(8888); while (true) { // 等待连接 Socket socket = serverSocket.accept(); // 读取数据 byte[] buffer = new byte[socket.getInputStream().available()]; socket.getInputStream().read(buffer); try { // 将字节数组反序列化为 Person 对象 Person person = Person.parseFrom(buffer); System.out.println(person); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } // 关闭连接 socket.shutdownInput(); socket.close(); } } } ``` 这样,就完成了 C#Java 之间通过 Socket 传输 Protobuf 字节实例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值