- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在尝试实现一个强大的 TCP 库,它将允许用户选择一个应用程序协议(protocol)或实现他们自己的应用程序协议(protocol)并简单地将它们“插入”到客户端/服务器中。
我所说的协议(protocol)只是指能够定义如何将流构建到消息中的能力。
我正在为堆栈的其余部分使用内置的异步 TCP 库,并开发了一个客户端,它会在建立连接、读取或写入数据或引发异常时引发事件。
我有两种实现框架协议(protocol)的选择。第一个已经在工作,它是扩展客户端类并覆盖数据接收事件,以便仅在接收到完整消息时引发此事件。 (即在后台我缓冲来自套接字的原始数据,并根据协议(protocol)决定何时收到完整消息,然后才引发数据接收事件。)这类似于 Nito.Asynch 的方式。图书馆作品。
这种方法的问题在于它意味着每个新协议(protocol)都需要一个新的客户端实现。我更希望客户端维护一个可以添加或删除的内部过滤器堆栈。
当在套接字上接收到数据时,它被传递到第一个过滤器,该过滤器进行缓冲,直到它决定传递一个完整的消息,并删除了 header 或元数据。然后将其传递给堆栈中的下一个过滤器等。
通过这种方式,可以独立于库定义/开发过滤器,并根据配置(在运行时)将其注入(inject)客户端。
为了实现这一点,我考虑将过滤器定义为 System.IO.Stream(传入和传出)的成对实现,它们由客户端内部保存。
从套接字读取的数据将写入堆栈底部的传入流。然后从该流读取的数据将写入下一个流等,直到最后一个流(堆栈顶部)返回数据,然后由客户端返回。 (我的计划是使用 Stream 的 CopyTo() 函数)。
写入客户端的数据将被写入到顶部的输出流并复制到堆栈中,直到底部的输出流写入底层套接字。
显然有很多需要考虑的问题,我正在努力思考如何以正确的方式将其作为 Stream 对象。示例:当有人调用 Flush()... 时我该怎么办?
这是实现此目标的好方法还是我在这里重新发明轮子?
Nito.Asynch 库
最佳答案
我正在回答我自己的问题,希望我的解决方案能得到一些好的评论并可能帮助其他人。
我为协议(protocol)过滤器和数据帧定义了两个接口(interface)。 (为了明确术语,我避免使用数据包一词,以免与较低级别协议(protocol)中定义的数据包混淆。)
虽然不是我自己的意图,但我猜这可以在任何传输协议(protocol)(即命名管道、TCP、串行)之上使用。
首先是数据框的定义。这包括“数据”(有效载荷)以及将传输数据构建为原子“消息”的任何字节。
/// <summary>
/// A packet of data with some form of meta data which frames the payload for transport in via a stream.
/// </summary>
public interface IFramedData
{
/// <summary>
/// Get the data payload from the framed data (excluding any bytes that are used to frame the data)
/// i.e. The received data minus protocl specific framing
/// </summary>
public readonly byte[] Data { get; }
/// <summary>
/// Get the framed data (payload including framing bytes) ready to send
/// </summary>
/// <returns>Framed data</returns>
public byte[] ToBytes();
}
然后是协议(protocol)过滤器,它从某个源(例如 TCP 套接字,或者如果它们在堆栈中使用的话,甚至是另一个过滤器)读取数据并将数据写回。
过滤器应读取数据(包括帧)并为每个完整的帧读取引发 DataReceived 事件。通过 IFramedData 实例的“数据”属性访问有效负载。
当数据被写入过滤器时,它应该适本地“构建”它,然后在每次准备好发送完整的数据帧时引发 DataToSend 事件。 (在我的例子中,这将是立竿见影的,但我试图允许一个协议(protocol),该协议(protocol)可能会发送固定长度的消息或出于其他原因缓冲输入,然后返回一个准备发送的完整帧。
/// <summary>
/// A protocol filter can be used to read and write data from/to a Stream and frame/deframe the messages.
/// </summary>
/// <typeparam name="TFramedData">The data frame that is handled by this filter</typeparam>
public interface IProtocolFilter<TFramedData> where TFramedData : IFramedData
{
/// <summary>
/// Should be raised whenever a complete data frame is ready to send.
/// </summary>
/// <remarks>
/// May be raised after a call to <see cref="FlushSend()"/>
/// </remarks>
public event Action<TFramedData> DataToSend;
/// <summary>
/// Should be raised whenever a complete data frame has been received.
/// </summary>
/// <remarks>
/// May be raised after a call to <see cref="FlushReceive()"/>
/// </remarks>
public event Action<TFramedData> DataReceived;
/// <summary>
/// Should be raised if any data written or read breaks the protocol.
/// This could be due to any asynchronous operation that cannot be raised by the calling function.
/// </summary>
/// <remarks>
/// Behaviour may be protocol specific such as flushing the read or write cache or even resetting the connection.
/// </remarks>
public event Action<Exception> ProtocolException;
/// <summary>
/// Read data into the recieve buffer
/// </summary>
/// <remarks>
/// This may raise the DataReceived event (possibly more than once if multiple complete frames are read)
/// </remarks>
/// <param name="buffer">Data buffer</param>
/// <param name="offset">Position within the buffer where data must start being read.</param>
/// <param name="count">Number of bytes to read.</param>
/// <returns></returns>
public int Read(byte[] buffer, int offset, int count);
/// <summary>
/// Write data to the send buffer.
/// </summary>
/// <remarks>
/// This may raise the DataToSend event (possibly more than once if the protocl requires the data is broken into multiple frames)
/// </remarks>
/// <param name="buffer">Data buffer</param>
/// <param name="offset">Position within the buffer where data must start being read.</param>
/// <param name="count">Number of bytes to read from the buffer</param>
public void Write(byte[] buffer, int offset, int count);
/// <summary>
/// Flush any data from the receive buffer and if appropriate, raise a DataReceived event.
/// </summary>
public void FlushReceive();
/// <summary>
/// Flush any data from the send buffer and if appropriate, raise a DataToSend event.
/// </summary>
public void FlushSend();
}
然后我围绕 TcpClient 编写了一个非常简单的包装器,它执行异步读取和写入,并在协议(protocol)栈顶部的过滤器引发 DataReceived 事件或底部的过滤器引发 DataToSend 事件时引发事件(我也写将数据发送到套接字,但这允许应用程序监视它写入客户端的数据何时实际发送)。
关于c# - 实现 TCP 成帧的正确模式是什么?它是一个过滤器堆栈吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11310455/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!