在Socket应用开发中,还有一个话题是讨论的比较多的,那就是数据接收后如何处理的问题。这也是一个令刚接触Socket开发的人很头疼的问题。
因为Socket的TCP通讯中有一个“粘包”的现象,既:大多数时候发送端多次发送的小数据包会被连在一起被接收端同时接收到,多个小包被组成一个大包被接收。有时候一个大数据包又会被拆成多个小数据包发送。这样就存在一个将数据包拆分和重新组合的问题。那么如何去处理这个问题呢?这就是我今天要讲的通讯协议。
所谓的协议就是通讯双方协商并制定好要传送的数据的结构与格式。并按制定好的格式去组合与分析数据。从而使数据得以被准确的理解和处理。
那么我们如何去制定通讯协议呢?很简单,就是指定数据中各个字节所代表的意义。比如说:第一位代表封包头,第二位代表封类型,第三、四位代表封包的数据长度。然后后面是实际的数据内容。
如下面这个例子:
01
|
01
|
06 00
|
01 0f ef 87 56 34
|
协议类别
|
协议代码
|
数据长度
|
实际数据
|
前面三部分称之为封包头,它的长度是固定的,第四部分是封包数据,它的长度是不固定的,由第三部分标识其长度。因为我们的协议将用在TCP中,所以我没有加入校验位。原因是TCP可以保证数据的完整性。校验位是没有必要存在的。
接下来我们要为这个数据封包声明一个类来封装它:
写道
public class Message
{
private byte _class;
private byte _flag;
private int _size;
private byte[] _content;
public byte[] Content
{
get { return _content; }
set { _content = value; }
}
public int Size
{
get { return _size; }
set { _size = value; }
}
public byte Flag
{
get { return _flag; }
set { _flag = value; }
}
public byte Class
{
get { return _class; }
set { _class = value; }
}
public Message()
{
}
public Message(byte @class, byte flag, byte[] content)
{
_class = @class;
_flag = flag;
_size = content.Length;
_content = content;
}
public byte[] ToBytes()
{
byte[] _byte;
using (MemoryStream mem = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(mem);
writer.Write(_class);
writer.Write(_flag);
writer.Write(_size);
if (_size > 0)
{
writer.Write(_content);
}
_byte = mem.ToArray();
writer.Close();
}
return _byte;
}
public static Message FromBytes(byte[] Buffer)
{
Message message = new Message();
using (MemoryStream mem = new MemoryStream(Buffer))
{
BinaryReader reader = new BinaryReader(mem);
message._class = reader.ReadByte();
message._flag = reader.ReadByte();
message._size = reader.ReadInt32();
if (message._size > 0)
{
message._content = reader.ReadBytes(message._size);
}
reader.Close();
}
return message;
}
}
我们可以用Tobytes()和FromBytes()将封包转换成二进制数组和从二进制数组转换回来。
事情看起来已经解决了,但……真的是这样子吗?不然,我们知道,TCP数据是以流的形式被传送的,我们并不知道一个数据包是否被传送完毕,也不知道我们接收回来的数据包中是否有多个数据包,如果直接使用FromBytes()来转换的话,很可能会因为数据不完整而出现异常,也有可能会因为数据中含有多个数据包而导致数据丢失(因为你并不知道这些数据中含有多少个数据包)。那我们该怎么办?这也不难,我们先把接收回来的数据写入一个流中。然后分析其中是否有完整的数据包,如果有,将其从流中取出,并将这部分数据从流中清除。直到流中没有完整的数据为止,以后接收回来的数据就将其写入流的结尾处,并从头继续分析。直到结束。
让我们来看看这部分的代码:
写道
public class MessageStream
2 {
3 private byte[] _buffer;
4 private int _position;
5 private int _length;
6 private int _capacity;
7
8 public MessageStream()
9 {
10 _buffer = new byte[0];
11 _position = 0;
12 _length = 0;
13 _capacity = 0;
14 }
15
16 private byte ReadByte()
17 {
18 if (this._position >= this._length)
19 {
20 return 0;
21 }
22 return this._buffer[this._position++];
23 }
24
25 private int ReadInt()
26 {
27 int num = this._position += 4;
28 if (num > this._length)
29 {
30 this._position = this._length;
31 return -1;
32 }
33 return (((this._buffer[num - 4] | (this._buffer[num - 3] << 8)) | (this._buffer[num - 2] << 0x10)) | (this._buffer[num - 1] << 0x18));
34 }
35
36 private byte[] ReadBytes(int count)
37 {
38 int num = this._length - this._position;
39 if (num > count)
40 {
41 num = count;
42 }
43 if (num <= 0)
44 {
45 return null;
46 }
47 byte[] buffer = new byte[num];
48 if (num <= 8)
49 {
50 int num2 = num;
51 while (--num2 >= 0)
52 {
53 buffer[num2] = this._buffer[this._position + num2];
54 }
55 }
56 else
57 {
58 Buffer.BlockCopy(this._buffer, this._position, buffer, 0, num);
59 }
60 this._position += num;
61 return buffer;
62 }
63
64 public bool Read(out Message message)
65 {
66 message = null;
67 _position = 0;
68 if (_length > 6)
69 {
70 message = new Message();
71 message.Class = ReadByte();
72 message.Flag = ReadByte();
73 message.Size = ReadInt();
74 if (message.Size <= 0 || message.Size <= _length - _position)
75 {
76 if (message.Size > 0)
77 {
78 message.Content = ReadBytes(message.Size);
79 }
80 Remove(message.Size + 6);
81 return true;
82 }
83 else
84 {
85 message = null;
86 return false;
87 }
88 }
89 else
90 {
91 return false;
92 }
93 }
94
95 private void EnsureCapacity(int value)
96 {
97 if (value <= this._capacity)
98 return;
99 int num1 = value;
100 if (num1 < 0x100)
101 num1 = 0x100;
102 if (num1 < (this._capacity * 2))
103 num1 = this._capacity * 2;
104 byte[] buffer1 = new byte[num1];
105 if (this._length > 0)
106 Buffer.BlockCopy(this._buffer, 0, buffer1, 0, this._length);
107 this._buffer = buffer1;
108 this._capacity = num1;
109 }
110
111 public void Write(byte[] buffer, int offset, int count)
112 {
113 if (buffer.Length - offset < count)
114 {
115 count = buffer.Length - offset;
116 }
117 EnsureCapacity(buffer.Length + count);
118 Array.Clear(_buffer, _length, _capacity - _length);
119 Buffer.BlockCopy(buffer, offset, _buffer, _length, count);
120 _length += count;
121 }
122
123 private void Remove(int count)
124 {
125 if (_length >= count)
126 {
127 Buffer.BlockCopy(_buffer, count, _buffer, 0, _length - count);
128 _length -= count;
129 Array.Clear(_buffer, _length, _capacity - _length);
130 }
131 else
132 {
133 _length = 0;
134 Array.Clear(_buffer, 0, _capacity);
135 }
136 }
137 }
这个类的使用非常简单,你只要用Write(byte[] buffer, int offset, int count)将接收到的数据写入数据流中,并用bool Read(out Message message)将数据中的第一个数据包取出,如果函数返回True,就说明取回一个封包成功,如果返回False,则说明流中已经没有完整的封包,你需要继续接收后面的数据以组成一个完整的封包。
这们我们的数据分析就会变得非常简单。我们可以在ReceiveCallBack回调函数中将接收到的数据写入到流中并通知线程池中的工作者线程分析数据流并处理数据。我在前面的关于Socket异步操作的文章中的Analyzer函数就是用这两个类来分析处理数据的。这样的好处理就是,Socket工作线程只需要负责数据的接收,并将其写入流,其它的事情由其它的线程这处理,就不会因为处理的时间过长而导致接收操作被阻塞。从而影响Socket的性能。
本文所述方法只是协议处理的多种方法中的其中一种,而且可能并不是很优秀的方法,如果谁有更好的方法,还希望您能和我多多交流。好了,今天就到这里了,关于Socket的文章到这里可能就告一段落了,我现在在研究VS2008里面的新东西,如果有什么必得的话,我会继续写出来的。谢谢大家的支持。
分享到:
相关推荐
Java Socket 开发即时通讯服务器是构建实时通信系统的关键技术之一,尤其在企业级应用、在线游戏、聊天室等场景中广泛应用。本篇将深入探讨Java Socket在即时通讯服务器开发中的核心概念、步骤以及关键技术。 首先...
在本场景中,我们关注的是PB9中实现的SOCKET通讯协议。SOCKET通讯协议是网络编程中的基础组件,它允许应用程序通过网络进行数据传输。以下是关于PB9和SOCKET通讯协议的详细知识: 1. **PowerBuilder 9基础知识**: ...
本文将深入探讨“C# Socket单端口多种通讯协议服务”这一主题,以及如何利用C# Socket实现这样的功能。 标题中的“C# Socket单端口多种通讯协议服务”是指使用C#的Socket类库来构建一个服务器,该服务器可以在单一...
本文将详细讨论“卫通星GPS定位器GT06”如何通过Socket通讯协议与Java Spring Boot应用程序进行对接,以及涉及的技术点。 首先,我们要了解卫通星GPS定位器GT06。这是一款支持多种通讯方式的设备,包括GSM/GPRS网络...
标题中的“GPS定位器GT06协议socket通讯JAVA源代码”揭示了本次讨论的主要内容,即使用Java编程语言实现GPS定位器(型号为GT06)的Socket通信协议。GPS定位器是用于获取地理位置信息的设备,而GT06协议是这种特定...
在本项目中,"C#使用SuperSocket实现自定义协议实现CS架构服务器和客户端程序设计"),开发者利用C#编程语言以及SuperSocket开源库构建了一套C/S(客户端/服务器)架构的应用。SuperSocket是一个轻量级、高度可扩展的...
Socket编程在IT行业中是网络通信的基础,特别是在C#这样的编程语言中,Socket是实现TCP通讯的重要工具。本示例提供了一个完整的C# Socket通讯/TCP通讯的代码demo,旨在帮助开发者理解和应用网络通信技术。 首先,...
3. **Socket通讯**:Socket通讯是一种基于TCP/IP协议的网络通信方式,允许不同计算机上的应用程序之间交换数据。在FANUC机器人与视觉系统的交互中,Socket通讯用于建立和维持两者之间的连接,收发数据,如rece_data....
在现代Web开发中,为了实现实时双向通信,JavaScript结合Socket技术(通常指的是WebSocket协议)成为一种常见且强大的解决方案。WebSocket是HTML5引入的一个新特性,允许服务器和浏览器之间建立长时间保持连接的通道...
通过学习和理解以上知识点,并结合提供的C# demo和C++ DLL,开发者可以快速掌握基于HPSocket的Socket通信技术,提高网络应用开发的效率和质量。无论是构建实时聊天系统、游戏服务器还是其他需要网络通信的应用,HP...
在Android平台上进行即时通讯(Instant Messaging,简称IM)开发,Socket通信是不可或缺的一部分。Socket提供了一种在网络中两台设备间建立可靠数据传输的机制,它允许Android客户端与服务器之间进行实时、双向的...
总的来说,卫通星GPS定位器GT06与Java Spring Boot的Socket通讯涉及到了网络编程、数据解析、框架集成等多个技术点。通过合理的架构设计和编码实践,我们可以构建出稳定、高效的GPS监控系统,满足物联网应用的需求。
Socket通信协议是网络编程中的重要概念,...掌握Socket通信协议及其应用,对于开发网络应用和理解网络原理至关重要。在实际项目中,开发者可以根据具体需求选择适当的协议(如UDP而非TCP)和编程语言实现Socket通信。
《短信网关Socket数据接口通讯协议(V0.1)》是一个详细定义了短信客户端与服务器端之间通过TCP短连接进行数据交互的规范。这个协议主要应用于互联网环境,以确保双方能够稳定、高效地进行短信服务的请求和响应。 在...
在IT领域,尤其是在软件开发中,串口通讯和Socket通讯是两种常见的通信方式,尤其在设备交互、嵌入式系统以及物联网(IoT)应用中非常常见。本篇将详细探讨C#环境下如何利用`SerialPort`类进行串口通讯以及如何使用`...
【亲测实用】c# socket与基恩士plc通讯、和扫码枪通讯程序源码 文件类型:程序源代码 主要功能: 工业通讯,c# socket与基恩士plc通讯、和扫码枪通讯 适合人群:新手及有一定经验的开发人员
4. **WCS与PLC的通讯协议**:在WCS与PLC通信时,需要定义一套通讯协议,包括数据格式、命令集、错误处理机制等,以确保信息的准确无误传输。常见的协议有MODBUS TCP/IP、EtherNet/IP、PROFINET等。 5. **源码示例**...
SocketTool是一款专为网络通讯开发设计的测试工具,它支持TCP/IP协议,既可以作为客户端运行,也可以作为服务端运行,极大地便利了开发者在日常工作中进行网络通信的测试和调试。这款工具具有以下主要特点和功能: ...
Socket框架在IT行业中是构建网络通信应用的基础,尤其在C#环境下,利用Socket进行实时通讯是常见的技术选择。本框架专注于TCP协议,确保了数据的可靠传输和长连接的维护,这对于需要持续交互的应用,如在线游戏、...
创建这样一个简易的Socket通讯工具,可以帮助开发者更好地理解网络通信过程,同时也可以作为实际项目开发中的基础模块。在实践中,还需要考虑更多的因素,比如多线程处理、错误恢复机制、数据编码解码等,以提升通信...