`
wlvfox
  • 浏览: 26061 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Socket开发之通讯协议及处理

阅读更多

Socket应用开发中,还有一个话题是讨论的比较多的,那就是数据接收后如何处理的问题。这也是一个令刚接触Socket开发的人很头疼的问题。

因为SocketTCP通讯中有一个“粘包”的现象,既:大多数时候发送端多次发送的小数据包会被连在一起被接收端同时接收到,多个小包被组成一个大包被接收。有时候一个大数据包又会被拆成多个小数据包发送。这样就存在一个将数据包拆分和重新组合的问题。那么如何去处理这个问题呢?这就是我今天要讲的通讯协议。

所谓的协议就是通讯双方协商并制定好要传送的数据的结构与格式。并按制定好的格式去组合与分析数据。从而使数据得以被准确的理解和处理。

那么我们如何去制定通讯协议呢?很简单,就是指定数据中各个字节所代表的意义。比如说:第一位代表封包头,第二位代表封类型,第三、四位代表封包的数据长度。然后后面是实际的数据内容。

如下面这个例子:

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里面的新东西,如果有什么必得的话,我会继续写出来的。谢谢大家的支持。

 

分享到:
评论

相关推荐

    c# socket与基恩士plc通讯、和扫码枪通讯程序源码

    【亲测实用】c# socket与基恩士plc通讯、和扫码枪通讯程序源码 文件类型:程序源代码 主要功能: 工业通讯,c# socket与基恩士plc通讯、和扫码枪通讯 适合人群:新手及有一定经验的开发人员

    C# Socket单端口多种通讯协议服务

    这是一个通讯服务和协议完全分离的框架,单端口允许多种通讯协议并存,除了内置的Http、WebSocket、Flex策略和Fast中间件,开发者还可以开发自己的中间件并安装到通讯服务。

    UDP协议通信socket编程(物联网).rar

    调研http协议、TCP协议、UDP协议及socket编程相关知识;根据课程设计要求,选择合适的操作系统、开发环境及测试环境 必需有界面窗口,客户端可以实现网址的录入,协议的选择(TCP或返回信息的显示。服务器端要有...

    三菱Q系列PLC TCP socket通讯实例程序加技术文档

    【老马出品,必属精品,质量保证,亲测能用】 资源名:三菱Q系列PLC TCP socket通讯实例程序加技术文档 资源类型:程序源代码 源码介绍:PLC 为三菱Q06H和QJ71E71-100模块;...适合人群:新手及有一定经验的开发人员

    Modbus协议与各类PLC通过socket通讯源码

    【工控老马出品,必属精品,质量保证,亲测能用】 资源名:Modbus协议与各类PLC通过socket通讯源码 资源类型:程序源代码 源码说明: 实例封装了modbus标准协议,通过socket...适合人群:新手及有一定经验的开发人员

    卫通星GPS定位器GT06协议socket通讯JAVA Spring Boot对接

    卫通星GPS定位器GT06协议socket通讯JAVA Spring Boot对接

    C# socket通讯Sample

    C# socket通讯Sample通过TCP协议的socket的通讯适合初学者学习。开发平台:VS2008

    SOCKET通讯程序

    本程序主要采用VC6.0的SOCKET编程,实现了点对点的即时通讯。基于TCP/IP协议

    vc 开发的支持串口与socket通信的DLL

    vc 写的dll .用于port 或socket 通信协议的封装, 很好的协议架构,适用于考勤机及工业相关软件协议的开发。

    socketTool网络通讯的测试工具

    经常做网络通讯开发的时候,免不了进行网络通讯的测试,这个工具不错,TCP/IP协议的客户端及服务端均可,使用很久了,是平常开发中的辅助测试工具。

    C++使用SOCKET实现TCP-IP协议的通讯最好的DEMO源码

    了解了TCPIP通讯的基本结构后,接下来讲解建立的流程,首先声明一下我用的开发环境是Visual Studio2008版的,语言C#,组件System.Net.Sockets,流程的建立包括服务器端的建立和客户端的建立,如图所示: 二、实现:...

    基于Socket的网络聊天系统开发与设计.doc

    本次设计主要以Socket网络通讯作为基础,并以Object作为通讯载体,同时运用了一定的通讯协议知识开发完成。在界面方面,采用的是Java中的Swing技术来实现。为了实现多用户的连接,在服务器端将采用多线程的技术来...

    基于Socket网络通信协议的应用研究

    随着网络通信技术的不断发展,Socket开发和研究已成为人们关注的热点。Socke既适用于同一台计算机上的进程间通信,也适用于网络环境中的进程间通信。它已成为当前许多操作系统的网络API,也是网络操作系统中必不可少...

    基于Java和Shell的卫通星GPS定位器GT06协议socket通讯JAVA Spring Boot对接设计源码

    本卫通星GPS定位器GT06协议socket通讯项目基于Java和Shell开发,包含60个文件,包括Java源代码、JPG图片、PDF文档、XML配置文件、GIT忽略文件、LICENSE文件、Markdown文档、Shell脚本、SQL脚本和YAML配置文件。...

    C#与三菱PLC 进行socket通信 程序源码

    【程序老媛出品,必属精品,亲测校正,质量保证】 资源名:C#与三菱PLC 进行socket通信 程序源码 资源类型:程序源代码 源码说明: C#与三菱FX5U socket通信,内有C#程序 三菱...适合人群:新手及有一定经验的开发人员

    SOCKET通信 MMORPG网页游戏开发经验

    SOCKET通信 MMORPG网页游戏开发经验

    基于Socket网络聊天系统的设计与实现

    基于Socket网络聊天系统的设计与实现是指使用Java应用程序设计,基于Client/Server模型,使用Eclipse作为开发环境,MySQL作为后台数据库,利用JDBC连接数据库的网络聊天系统。该系统主要包括服务器模块和客户端模块...

    牛逼抓包工具 Socket通讯抓包

    很好用的抓包工具 实现Socket客户端,服务端,转发器调用测试 很实用 Socket开发的最佳工具

    OMRON以太网TCP通讯格式.doc

    基于欧姆龙PLC的以太网通讯格式的详细收发测试及研究文档,以太网收发测试工具TCP/UDP Socket 调试工具V2.2.(若...)通过收发监控实例,可以掌握欧姆龙PLC以太网通讯协议格式,便于自行完成上位机软件的通讯开发工作。

Global site tag (gtag.js) - Google Analytics