我的目标是从套接字读取消息,其中每条消息都用ETX字符分隔。它是一个高频市场数据馈送,所以我不认为逐字节方法是有道理的,也是完整消息的大小是未知的。
有没有办法可以使用NetworkStream
类来阅读此消息?我也尝试使用Socket
类来实现此目的,但不是从socket中逐个读取消息,而是从套接字读取所有消息,随着系统速度变慢,这成为一个问题。
答案 0 :(得分:5)
我们走了;这是用于从Socket
或Stream
等来源读取标记分隔的邮件列表的基本过程。棘手的一点是跟踪你在传入缓冲区中使用的内容,以及来自早期缓冲区的任何未使用数据的积压。请注意,在Socket
和Stream
之间更改此代码实质上是将Receive
更改为Read
- 除此之外,方法也是相同的。
以下应该基本上做你需要的。您可以使用ReadNext()
API,直到获得null
(表示流的结尾),或者您可以使用ReadAll()
来提供IEnumerable<string>
序列。编码和缓冲区大小可供您通过构造函数进行调整,但默认为合理的值。
foreach (var s in reader.ReadAll())
Console.WriteLine(s);
代码:
class EtxReader : IDisposable
{
public IEnumerable<string> ReadAll()
{
string s;
while ((s = ReadNext()) != null) yield return s;
}
public void Dispose()
{
if (socket != null) socket.Dispose();
socket = null;
if (backlog != null) backlog.Dispose();
backlog = null;
buffer = null;
encoding = null;
}
public EtxReader(Socket socket, Encoding encoding = null, int bufferSize = 4096)
{
this.socket = socket;
this.encoding = encoding ?? Encoding.UTF8;
this.buffer = new byte[bufferSize];
}
private Encoding encoding;
private Socket socket;
int index, count;
byte[] buffer;
private bool ReadMore()
{
index = count = 0;
int bytes = socket.Receive(buffer);
if (bytes > 0)
{
count = bytes;
return true;
}
return false;
}
public const byte ETX = 3;
private MemoryStream backlog = new MemoryStream();
public string ReadNext()
{
string s;
if (count == 0)
{
if (!ReadMore()) return null;
}
// at this point, we expect there to be *some* data;
// this may or may not include the ETX terminator
var etxIndex = Array.IndexOf(buffer, ETX, index);
if (etxIndex >= 0)
{
// found another message in the existing buffer
int len = etxIndex - index;
s = encoding.GetString(buffer, index, len);
index = etxIndex + 1;
count -= (len + 1);
return s;
}
// no ETX in the buffer, so we'll need to fetch more data;
// buffer the unconsumed data that we have
backlog.SetLength(0);
backlog.Write(buffer, index, count);
bool haveEtx;
do
{
if (!ReadMore())
{
// we had unused data; this must signal an error
throw new EndOfStreamException();
}
etxIndex = Array.IndexOf(buffer, ETX, index);
haveEtx = etxIndex >= 0;
if (!haveEtx)
{
// keep buffering
backlog.Write(buffer, index, count);
}
} while (!haveEtx);
// now we have some data in the backlog, and the ETX in the buffer;
// for convenience, copy the rest of the next message into
// the backlog
backlog.Write(buffer, 0, etxIndex);
s = encoding.GetString(backlog.GetBuffer(), 0, (int)backlog.Length);
index = etxIndex + 1;
count -= (etxIndex + 1);
return s;
}
}
答案 1 :(得分:2)
这可能是基于文本的API。使用NetworkStream
与Socket
之间没有实际区别; Stream
和Socket
都不会读取所有消息&#34; - 它只是你的代码。
在这两种情况下,你都需要一个几乎相同的循环来获取下一个数据块(这不是&#34;消息&#34;的同义词),并开始寻找你的哨兵值(你的意思是ETX
?) - 根据需要处理或缓冲。除非您知道传入的Feed是单字节编码,否则您可能最好将其视为字节,直到您实际将其拆分为逻辑消息,然后 运行文本解码器在它移动到下一个之前获取此消息的文本。
答案 2 :(得分:1)
您应该研究异步通信和TcpListener类。我的方法是:
BeginAccept
/ EndAccecpt
)。NetworkStream
异步读取,直到客户端断开连接(BeginRead
/ EndRead
)。您可以读取数据块,例如,您可以尝试一次读取512个字节 - 如果缓冲区中的字节少于512个字节,则您将获得少于512个字节。StringBuilder
的内容(每个连接一个,在将byte[]
转换为string
时注意正确的编码)StringBuilder
包含分隔符,请将该消息拆分并将其写入队列(在排队之前不要忘记锁定队列!)ManualResetEvent
这只是一个粗略的概述,但我相信你明白了。
没有阅读“消息”的东西 - 通过TCP / IP传入的所有东西只是一个字节流 - 这就是你获得网络流的原因。消息是您发明的用于解释数据的概念。