C# TcpListener+;TcpClient-在关闭前等待客户端读取数据
我正在使用TcpClient为PDF文件构建一个简单的HTTP服务器。它工作得很好,但是TcpClient在浏览器下载PDF完成之前关闭。如何强制TcpClient在关闭前等待远程客户端获得所有写入的内容C# TcpListener+;TcpClient-在关闭前等待客户端读取数据,c#,.net,tcpclient,C#,.net,Tcpclient,我正在使用TcpClient为PDF文件构建一个简单的HTTP服务器。它工作得很好,但是TcpClient在浏览器下载PDF完成之前关闭。如何强制TcpClient在关闭前等待远程客户端获得所有写入的内容 //pdf is byte[] TcpListener server = new TcpListener(address, port); server.Start(); TcpClient client = server.AcceptTcpClient(); //Wait for co
//pdf is byte[]
TcpListener server = new TcpListener(address, port);
server.Start();
TcpClient client = server.AcceptTcpClient(); //Wait for connection
var ns = client.GetStream();
string headers;
using (var writer = new StringWriter())
{
writer.WriteLine("HTTP/1.1 200 OK");
//writer.WriteLine("Accept: text/html");
writer.WriteLine("Content-type: application/pdf");
writer.WriteLine("Content-length: " + pdf.Length);
writer.WriteLine();
headers = writer.ToString();
}
var bytes = Encoding.UTF8.GetBytes(headers);
ns.Write(bytes, 0, bytes.Length);
ns.Write(pdf, 0, pdf.Length);
Thread.Sleep(TimeSpan.FromSeconds(10)); //Adding this line fixes the problem....
client.Close();
server.Stop();
我能换掉那个难看的“睡眠线”吗
编辑:以下代码根据答案起作用:
TcpListener Server = null;
public void StartServer()
{
Server = new TcpListener(IPAddress.Any, Port);
Server.Start();
Server.BeginAcceptTcpClient(AcceptClientCallback, null);
}
void AcceptClientCallback(IAsyncResult result)
{
var client = Server.EndAcceptTcpClient(result);
var ns = client.GetStream();
string headers;
byte[] pdf = //get pdf
using (var writer = new StringWriter())
{
writer.WriteLine("HTTP/1.1 200 OK");
//writer.WriteLine("Accept: text/html");
writer.WriteLine("Content-type: application/pdf");
writer.WriteLine("Content-length: " + pdf.Length);
writer.WriteLine();
headers = writer.ToString();
}
var bytes = Encoding.UTF8.GetBytes(headers);
ns.Write(bytes, 0, bytes.Length);
ns.Write(pdf, 0, pdf.Length);
client.Client.Shutdown(SocketShutdown.Send);
byte[] buffer = new byte[1024];
int byteCount;
while ((byteCount = ns.Read(buffer, 0, buffer.Length)) > 0)
{
}
client.Close();
Server.Stop();
}
代码中的主要问题是,您的服务器(文件主机)忽略了从它将文件写入的套接字中读取,因此无法检测(更不用说等待)客户端关闭连接 代码可能会更好,但至少您可以在
client.Close()之前添加类似的内容使其正常工作代码>语句:
//指示正在发送的字节的结尾
ns.Socket.Shutdown(SocketShutdown.Send);
//任意大小的缓冲区…很可能不会写入任何内容
字节[]缓冲区=新字节[4096];
int字节数;
而((字节数=ns.Read(buffer,0,buffer.Length))>0)
{
//忽略此处读取的任何数据
}
当一个端点启动一个优雅的闭包时(例如,通过调用Socket.Shutdown(SocketShutdown.Send);
),这将允许网络层识别数据流的结尾。一旦另一个端点读取了远程端点发送的所有剩余字节,下一个读取操作将以零字节长度完成。这是另一个端点的信号,表示已到达流的末尾,是时候关闭连接了
任一端点都可以启动带有“发送”关闭原因的优雅关闭。另一个端点可以在完成发送它想要发送的任何内容后通过使用“两者”关闭原因来确认它,在这一点上,两个端点都可以关闭它们的套接字(或流或侦听器,或它们可能正在使用的任何其他更高级别的抽象)
当然,在正确实现的协议中,您将提前知道远程端点是否会发送任何数据。如果您知道永远都不会有,那么您可以使用零长度缓冲区,如果您知道一些数据可能会从客户端发送回来,那么您实际上会对这些数据做一些事情(与上面的空循环体相反)
在任何情况下,上面的内容都是为了让您发布的已经很糟糕的代码正常工作。请不要将其误认为是产品质量代码中的内容
尽管如此,你发布的代码还远没有那么好。您不仅要实现一个基本的TCP连接,而且显然要尝试重新实现HTTP协议。这样做没有任何意义,因为.NET已经内置了HTTP服务器功能(参见示例)。如果您确实打算重新发明HTTP服务器,那么您需要比您发布的代码多得多的代码。缺少错误处理本身就是一个主要缺陷,它会引起各种各样的麻烦
如果您打算编写低级网络代码,您应该做更多的研究和实验。一个很好的资源是。当然,它的主要关注点是针对Winsock API的程序员。但是这里也有大量的通用信息,而且在任何情况下,各种套接字API都非常相似,因为它们都基于相同的低级概念
您可能还想查看各种现有的堆栈溢出问题解答。这里有几个与您的具体问题密切相关的问题解答:
不过一定要小心。外面的坏建议几乎和好建议一样多。不缺少像网络编程专家一样四处走动的人,而他们不是,所以对你读到的每一篇文章(包括我上面的建议!)都要小心谨慎。监听器应该向客户端发送一条消息,表明它获得了所有的数据。然后,客户端应该在收到消息后关闭。当客户端关闭连接时,服务器将自动关闭,这样您就不必在服务器端执行任何操作。@jdweng:在上面的代码中,侦听器正在发送数据,而不是接收数据。在任何情况下,都不需要确认数据。接收端点只需关闭连接(例如调用Socket.shutdown(SocketShutdown.Send)
)即可指示连接已完成。本地端点也应该这样做,以便远程端点知道数据流何时完成。您绝对需要确认消息。如果你不同意,我建议你读一本好的传播理论书。服务器不应关闭连接。它是奴隶。客户机是主机,而服务器所要做的就是接受命令、处理数据和返回响应。@jdweng:恕我直言,你不知道自己在说什么。一旦建立了TCP连接,唯一的硬性规定是两个端点都需要就其使用的协议达成一致。如果这意味着服务器(侦听端)只是发送一个数据流,然后关闭带有“发送”原因的套接字,那么这就是需要做的一切。无需明确确认收到数据;一旦客户端获得了所有数据,就足以以“两者”的理由关闭客户端,然后两个端点都可以关闭套接字。@jdweng:是的,关于实现服务器/客户端体系结构的“最佳”方式有各种“理论”。但这里唯一重要的是什么会起作用,以及TCP/IP协议支持什么。你的说法与事实不符。我知道代码很糟糕。使用更糟糕。我试图通过WebBrowser控件在WPF应用程序中显示PDF,我试图避免1)临时文件和2)HTTP保留等。欢迎提供任何建议。如果一切正常