使用 TcpClient/TcpListener,接收未知数量的单独消息的最佳方法是什么?
Using TcpClient/TcpListener, what is the best method to receive an unknown amount of separate messages?
我当前的 ClientHandler
方法接收一个 TcpClient
,在所述客户端上打开一个 NetStream
,然后像这样开始从中提取数据:
try
{
//byte[] bytes = new byte[client.ReceiveBufferSize];
byte[] bytes = new byte[100];
int bytes_read = netstream.Read(bytes, 0, bytes.Length);
Console.WriteLine(DateTime.Now.ToString() + ": Bytes Read");
while ( bytes_read > 0)
{
memstream.Write(bytes, 0, bytes_read);
bytes_read = netstream.Read(bytes, 0, bytes.Length);
if (memstream.Length > 255)
{
MemStreamRead(memstream);
memstream.Flush();
OkResponse(netstream);
}
}
}
catch (Exception e)
{
Console.WriteLine("No longer connected");
connected = false;
Console.WriteLine(e);
Console.WriteLine(e.StackTrace);
}
我已经可以从客户端获取第一条消息,但我很难考虑如何为新消息保持流打开状态,并决定何时完成当前消息。
netstream.DataAvailable
似乎可以工作,我假设它检测到 netstream
不再获取更多数据,而 returns 错误。但是,一旦有新数据可用,我该如何重新开始收听呢?
在此先感谢您的帮助!
how do I start listening again once new data is available?
再次致电Read
。这就是大多数循环的工作方式,只需调用非阻塞 Read
(或专用线程上的阻塞 Read
),然后在获取数据时对其进行处理。
when my current message is done
那是你遇到的另一个问题。当 OS 套接字缓冲区中没有更多数据时,您的消息不一定完成,当它按照您的规则定义完成时就完成了。您通常会使用消息长度打开消息,然后不断向 OS 请求数据,直到您填充了给定长度的缓冲区(或由于超时而失败)。
将 TcpClient
对象及其 netstream
保存在您可以反复使用的地方。然后根据需要经常调用 netstream.Read()
(非阻塞)。 DataAvailable
有数据读取时会告诉你。
注意:传入的 TCP 数据被添加到一个大缓冲区中,不能像接收到的那样分离回数据包。
使用足够大的大小调用 Read()
将从缓冲区中获取 'all' 数据。
您将需要自己的更高级别的协议来分离它。易于实施...
另一方面,在 UDP 上,Read()
将 return 1 个数据包的数据。在 Read()
之后将 return 下一个数据包的数据。保留分组'structure'。
对于TCP高层协议,你可以这样做:
- 以 1 个或多个 0xFF 字节开始每条消息(根据 您的 定义的消息,长度可能为 1 字节或 100.000 字节),因此您可以轻松检测到消息开始。
- 以 0x02(STX,含义为 'Start of text')开始,以 0x03(ETX,文本结尾)结束,因此您可以排除 'nonsense' 数据(例如活动消息)周围没有STX、ETX。
- 如果您要发送可能包含 0x02、0x03、0xFF 的二进制数据,则每条消息都可以以 nnnnnn 开头,nnnnnn 是一个数字,以字节为单位定义消息长度。废话数据不能在这里分开。
编辑:
另行通知:
如果连接被远端关闭,Read()
仍然会执行成功,但不会执行return数据。您必须以另一种方式找出连接是否即将关闭。有关更多有用信息,请参阅我对其他 SO 网络问题的回答。
我当前的 ClientHandler
方法接收一个 TcpClient
,在所述客户端上打开一个 NetStream
,然后像这样开始从中提取数据:
try
{
//byte[] bytes = new byte[client.ReceiveBufferSize];
byte[] bytes = new byte[100];
int bytes_read = netstream.Read(bytes, 0, bytes.Length);
Console.WriteLine(DateTime.Now.ToString() + ": Bytes Read");
while ( bytes_read > 0)
{
memstream.Write(bytes, 0, bytes_read);
bytes_read = netstream.Read(bytes, 0, bytes.Length);
if (memstream.Length > 255)
{
MemStreamRead(memstream);
memstream.Flush();
OkResponse(netstream);
}
}
}
catch (Exception e)
{
Console.WriteLine("No longer connected");
connected = false;
Console.WriteLine(e);
Console.WriteLine(e.StackTrace);
}
我已经可以从客户端获取第一条消息,但我很难考虑如何为新消息保持流打开状态,并决定何时完成当前消息。
netstream.DataAvailable
似乎可以工作,我假设它检测到 netstream
不再获取更多数据,而 returns 错误。但是,一旦有新数据可用,我该如何重新开始收听呢?
在此先感谢您的帮助!
how do I start listening again once new data is available?
再次致电Read
。这就是大多数循环的工作方式,只需调用非阻塞 Read
(或专用线程上的阻塞 Read
),然后在获取数据时对其进行处理。
when my current message is done
那是你遇到的另一个问题。当 OS 套接字缓冲区中没有更多数据时,您的消息不一定完成,当它按照您的规则定义完成时就完成了。您通常会使用消息长度打开消息,然后不断向 OS 请求数据,直到您填充了给定长度的缓冲区(或由于超时而失败)。
将 TcpClient
对象及其 netstream
保存在您可以反复使用的地方。然后根据需要经常调用 netstream.Read()
(非阻塞)。 DataAvailable
有数据读取时会告诉你。
注意:传入的 TCP 数据被添加到一个大缓冲区中,不能像接收到的那样分离回数据包。
使用足够大的大小调用 Read()
将从缓冲区中获取 'all' 数据。
您将需要自己的更高级别的协议来分离它。易于实施...
另一方面,在 UDP 上,Read()
将 return 1 个数据包的数据。在 Read()
之后将 return 下一个数据包的数据。保留分组'structure'。
对于TCP高层协议,你可以这样做:
- 以 1 个或多个 0xFF 字节开始每条消息(根据 您的 定义的消息,长度可能为 1 字节或 100.000 字节),因此您可以轻松检测到消息开始。
- 以 0x02(STX,含义为 'Start of text')开始,以 0x03(ETX,文本结尾)结束,因此您可以排除 'nonsense' 数据(例如活动消息)周围没有STX、ETX。
- 如果您要发送可能包含 0x02、0x03、0xFF 的二进制数据,则每条消息都可以以 nnnnnn 开头,nnnnnn 是一个数字,以字节为单位定义消息长度。废话数据不能在这里分开。
编辑:
另行通知:
如果连接被远端关闭,Read()
仍然会执行成功,但不会执行return数据。您必须以另一种方式找出连接是否即将关闭。有关更多有用信息,请参阅我对其他 SO 网络问题的回答。