在.NET中从NetworkStream读取的正确方法是什么

我一直在为此苦苦挣扎,找不到我无法从我也编写的TCP服务器正确读取代码的原因。我正在使用TcpClient该类及其GetStream()方法,但某些功能无法正常工作。操作可能会无限期阻塞(上次读取操作未按预期超时),或者数据被裁剪(由于某种原因,读取操作将返回0并退出循环,也许服务器的响应速度不够快)。这是实现此功能的三种尝试:

// this will break from the loop without getting the entire 4804 bytes from the server 

string SendCmd(string cmd, string ip, int port)

{

var client = new TcpClient(ip, port);

var data = Encoding.GetEncoding(1252).GetBytes(cmd);

var stm = client.GetStream();

stm.Write(data, 0, data.Length);

byte[] resp = new byte[2048];

var memStream = new MemoryStream();

int bytes = stm.Read(resp, 0, resp.Length);

while (bytes > 0)

{

memStream.Write(resp, 0, bytes);

bytes = 0;

if (stm.DataAvailable)

bytes = stm.Read(resp, 0, resp.Length);

}

return Encoding.GetEncoding(1252).GetString(memStream.ToArray());

}

// this will block forever. It reads everything but freezes when data is exhausted

string SendCmd(string cmd, string ip, int port)

{

var client = new TcpClient(ip, port);

var data = Encoding.GetEncoding(1252).GetBytes(cmd);

var stm = client.GetStream();

stm.Write(data, 0, data.Length);

byte[] resp = new byte[2048];

var memStream = new MemoryStream();

int bytes = stm.Read(resp, 0, resp.Length);

while (bytes > 0)

{

memStream.Write(resp, 0, bytes);

bytes = stm.Read(resp, 0, resp.Length);

}

return Encoding.GetEncoding(1252).GetString(memStream.ToArray());

}

// inserting a sleep inside the loop will make everything work perfectly

string SendCmd(string cmd, string ip, int port)

{

var client = new TcpClient(ip, port);

var data = Encoding.GetEncoding(1252).GetBytes(cmd);

var stm = client.GetStream();

stm.Write(data, 0, data.Length);

byte[] resp = new byte[2048];

var memStream = new MemoryStream();

int bytes = stm.Read(resp, 0, resp.Length);

while (bytes > 0)

{

memStream.Write(resp, 0, bytes);

Thread.Sleep(20);

bytes = 0;

if (stm.DataAvailable)

bytes = stm.Read(resp, 0, resp.Length);

}

return Encoding.GetEncoding(1252).GetString(memStream.ToArray());

}

最后一个“有效”,但是考虑到套接字已经支持读取超时,将硬编码的睡眠放入循环中看起来确实很丑陋!我需要设置的一些属性(IES)TcpClientNetworkStream?问题是否出在服务器上?服务器不关闭连接,这取决于客户端。上面的代码也在UI线程上下文(测试程序)中运行,也许与此有关……

NetworkStream.Read在没有更多数据可用之前,有人知道如何正确使用读取数据吗?我想我想要的是像旧的Win32

winsock超时属性…之类的东西ReadTimeout。它尝试读取直到达到超时,然后返回0

…但是有时似乎应该在数据应返回0的情况下返回。可用(或在途中..如果可用,读取是否可以返回0?),然后在数据不可用时在最后一次读取时无限期阻塞…

是的,我很茫然!

回答:

设置底层套接字ReceiveTimeout属性就可以了。您可以像这样访问它:yourTcpClient.Client.ReceiveTimeout。您可以阅读文档以获取更多信息。

现在,只要某些数据到达套接字,该代码将仅“hibernate”,否则,如果在读取操作开始时超过20ms,如果没有数据到达,它将引发异常。如果需要,我可以调整此超时时间。现在,我不必为每次迭代付出20毫秒的代价,而只是在最后一次读取操作时才付出代价。因为我在从服务器读取的第一个字节中具有消息的内容长度,所以我可以使用它来进一步调整它,并且如果已经收到所有期望的数据,则不尝试读取。

我发现使用ReceiveTimeout比实现异步读取要容易得多…这是工作代码:

string SendCmd(string cmd, string ip, int port)

{

var client = new TcpClient(ip, port);

var data = Encoding.GetEncoding(1252).GetBytes(cmd);

var stm = client.GetStream();

stm.Write(data, 0, data.Length);

byte[] resp = new byte[2048];

var memStream = new MemoryStream();

var bytes = 0;

client.Client.ReceiveTimeout = 20;

do

{

try

{

bytes = stm.Read(resp, 0, resp.Length);

memStream.Write(resp, 0, bytes);

}

catch (IOException ex)

{

// if the ReceiveTimeout is reached an IOException will be raised...

// with an InnerException of type SocketException and ErrorCode 10060

var socketExept = ex.InnerException as SocketException;

if (socketExept == null || socketExept.ErrorCode != 10060)

// if it's not the "expected" exception, let's not hide the error

throw ex;

// if it is the receive timeout, then reading ended

bytes = 0;

}

} while (bytes > 0);

return Encoding.GetEncoding(1252).GetString(memStream.ToArray());

}

以上是 在.NET中从NetworkStream读取的正确方法是什么 的全部内容, 来源链接: utcz.com/qa/432325.html

回到顶部