我一直在努力处理这个问题,但找不到代码无法正确从我编写的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());
}
最后一个“有效”,但是在循环内部放置硬编码的睡眠看起来确实很丑,考虑到套接字已经支持读取超时!我需要在TcpClient
或者NetworkStream
上设置一些属性吗?问题出在服务器上吗?服务器不会关闭连接,这取决于客户端。以上代码还在UI线程上下文(test program)中运行,也许与此有关...
有人知道如何正确使用NetworkStream.Read
读取数据直到没有更多数据可用吗?我想我希望得到类似于旧的Win32 winsock超时属性... ReadTimeout
等。它尝试读取直到达到超时时间,然后返回0... 但是有时当数据可用时(或正在途中时)它似乎会返回0,并且在没有数据可用时它会无限期地阻塞最后一次读取...
是的,我束手无策!
using (var client = new System.Net.Sockets.TcpClient(ip, port)) using (var stm = client.GetStream())
然后将方法的其余部分括在大括号内。这将确保在方法退出时,无论原因如何,连接都将关闭,资源将被回收。 - Stéphane GourichonTCPClient
会释放流。我曾经看到过由于程序不够小心(因为其他原因)而进行数千次连接时出现连接失败的情况。为什么要在这里偏离通常的IDisposable模式?实现IDisposable容易出错,而使用using()
则简单且安全。 - Stéphane Gourichon