PushStreamContent 没有发送最后的 zero-length 块来表示流结束
PushStreamContent isn't sending the final zero-length chunk to signal the end of stream
我需要 bulk-export 使用 Odata 协议通过 WebAPI 获取内容。我们正在尝试使用 PushStreamContent 将结果直接从数据库中流出。当我 运行 本地 IIS 实例中的服务运行良好时,但当我将其推送到服务器时,它将流式传输数据并在最后 2 KB 处暂停和“挂起”。
我通过跟踪文件大小验证了这一点。在本地 运行 上,我会得到一个 9094KB 的文件,当我将相同的代码部署到服务器时,我得到 9092KB,然后连接保持打开状态,传输停止。如果我终止客户端并查看文件,我将看到 json 流在写入过程中被切断。此外,我可以查看 IIS 中打开的连接并看到该连接仍处于活动状态。
无论如何,为什么 PushStreamContent 只是停止发送数据而不是关闭流?如果发生错误,流和连接将关闭。
public HttpResponseMessage GetBulkExport(ODataQueryOptions<vwBulkExport> options)
{
var reportData = options.ApplyTo(dbContext.vwBulkExport, new ODataQuerySettings() { EnsureStableOrdering = false });
return new ResponseStreamer(Request).StreamAsync(reportData);
}
public class ResponseStreamer
{
private HttpRequestMessage request;
public ResponseStreamer(HttpRequestMessage request)
{
this.request = request;
}
public HttpResponseMessage StreamAsync(IQueryable data)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
int counter = 0;
foreach (var item in data)
{
counter++;
string json = JsonConvert.SerializeObject(item);
var buffer = Encoding.UTF8.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
if (counter == 10)
{
counter = 0;
await outputStream.FlushAsync();
}
}
}
finally
{
await outputStream.FlushAsync();
outputStream.Close();
outputStream.Dispose();
}
});
return response;
}
}
这是我的客户端代码
using (var writer = File.OpenWrite("C:\temp\" + Guid.NewGuid().ToString()))
{
var client = new RestClient("http://localhost");
var url = "/odata/BulkExport";
var request = new RestRequest(url);
request.AddHeader("authorization", string.Format("Bearer {0}", authToken));
request.ResponseWriter = (responseStream) => responseStream.CopyTo(writer);
var response = client.DownloadData(request);
}
更新
我已经做了广泛的测试,我认为正在发生的是流永远不会关闭(因此最后一个块永远不会发送过来)我通过将上面的数据迭代更改为这个来得出这个结论:
for (int count = 0; count < 1000; count++) //foreach (var item in data)
{
string json = JsonConvert.SerializeObject(count.ToString()) + Environment.NewLine;
var buffer = Encoding.Default.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
}
我看到的是它只会 return 600“行”。似乎又丢失了 2 KB。然后我将循环更改为 count <601
并传输了整个流,但流从未关闭。我认为正在发生的是内部缓冲区大小,如果在 4K 左右(这是打印出的数字 0-600 的大小),并且由于流没有关闭,所以永远不会收到最后几个字节。这有意义吗?
无论如何,为什么流不关闭?我终于有了它,我没有看到任何错误抛出。
更新
我发现了更多信息。 HTTP 1.1 规范规定分块流需要以零长度块结束。经过一番挖掘后,我发现它 应该 正在发生,但无论出于何种原因,它都没有发生。在我的客户端中,我删除了 Connection: Keep-Alive
header 并将其替换为 Connection: Close
我遇到了同样的问题, 但是 一旦我强制连接到关闭(通过关闭我的测试应用程序)最后几个字节写入磁盘,一切都很好。这就是我知道没有发送零长度块的方式。
那么现在问题就变成了。 为什么当我关闭流时最后的 zero-length 块没有被发送? 从我读到的,调用 HttpContext.Current.ApplicationInstance.CompleteRequest();
应该强制请求结束并写出那一块。我将其添加为我的 finally 块中的最后一行,使用调试器我知道它是 运行ning。但是,该块仍未设置。
请记住,所有这些都适用于我托管在 IIS 中的开发机器,但不适用于 Web 服务器。
我的机器是 运行ning Windows 10,我安装了 asp.net 5,IIS7 全部默认。
Web 服务器是 Windows 服务器(我不确定版本)运行 IIS8 但 它只安装了 asp.net 4.5。 我最初的预感是这就是问题所在 - asp.net 框架的不同版本,但我检查了一下,该项目仅针对 ASP.NET 4.5。我仍然会尝试更新服务器,但由于我的目标是 4.5,我认为它不会有任何好处。
我认为问题是由于 .net 框架中的一个错误。将服务器更新到 .net 4.6 后,它正在运行。
这是我应用的补丁。 https://www.microsoft.com/en-us/download/details.aspx?id=48137
我需要 bulk-export 使用 Odata 协议通过 WebAPI 获取内容。我们正在尝试使用 PushStreamContent 将结果直接从数据库中流出。当我 运行 本地 IIS 实例中的服务运行良好时,但当我将其推送到服务器时,它将流式传输数据并在最后 2 KB 处暂停和“挂起”。
我通过跟踪文件大小验证了这一点。在本地 运行 上,我会得到一个 9094KB 的文件,当我将相同的代码部署到服务器时,我得到 9092KB,然后连接保持打开状态,传输停止。如果我终止客户端并查看文件,我将看到 json 流在写入过程中被切断。此外,我可以查看 IIS 中打开的连接并看到该连接仍处于活动状态。
无论如何,为什么 PushStreamContent 只是停止发送数据而不是关闭流?如果发生错误,流和连接将关闭。
public HttpResponseMessage GetBulkExport(ODataQueryOptions<vwBulkExport> options)
{
var reportData = options.ApplyTo(dbContext.vwBulkExport, new ODataQuerySettings() { EnsureStableOrdering = false });
return new ResponseStreamer(Request).StreamAsync(reportData);
}
public class ResponseStreamer
{
private HttpRequestMessage request;
public ResponseStreamer(HttpRequestMessage request)
{
this.request = request;
}
public HttpResponseMessage StreamAsync(IQueryable data)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
int counter = 0;
foreach (var item in data)
{
counter++;
string json = JsonConvert.SerializeObject(item);
var buffer = Encoding.UTF8.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
if (counter == 10)
{
counter = 0;
await outputStream.FlushAsync();
}
}
}
finally
{
await outputStream.FlushAsync();
outputStream.Close();
outputStream.Dispose();
}
});
return response;
}
}
这是我的客户端代码
using (var writer = File.OpenWrite("C:\temp\" + Guid.NewGuid().ToString()))
{
var client = new RestClient("http://localhost");
var url = "/odata/BulkExport";
var request = new RestRequest(url);
request.AddHeader("authorization", string.Format("Bearer {0}", authToken));
request.ResponseWriter = (responseStream) => responseStream.CopyTo(writer);
var response = client.DownloadData(request);
}
更新
我已经做了广泛的测试,我认为正在发生的是流永远不会关闭(因此最后一个块永远不会发送过来)我通过将上面的数据迭代更改为这个来得出这个结论:
for (int count = 0; count < 1000; count++) //foreach (var item in data)
{
string json = JsonConvert.SerializeObject(count.ToString()) + Environment.NewLine;
var buffer = Encoding.Default.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
}
我看到的是它只会 return 600“行”。似乎又丢失了 2 KB。然后我将循环更改为 count <601
并传输了整个流,但流从未关闭。我认为正在发生的是内部缓冲区大小,如果在 4K 左右(这是打印出的数字 0-600 的大小),并且由于流没有关闭,所以永远不会收到最后几个字节。这有意义吗?
无论如何,为什么流不关闭?我终于有了它,我没有看到任何错误抛出。
更新
我发现了更多信息。 HTTP 1.1 规范规定分块流需要以零长度块结束。经过一番挖掘后,我发现它 应该 正在发生,但无论出于何种原因,它都没有发生。在我的客户端中,我删除了 Connection: Keep-Alive
header 并将其替换为 Connection: Close
我遇到了同样的问题, 但是 一旦我强制连接到关闭(通过关闭我的测试应用程序)最后几个字节写入磁盘,一切都很好。这就是我知道没有发送零长度块的方式。
那么现在问题就变成了。 为什么当我关闭流时最后的 zero-length 块没有被发送? 从我读到的,调用 HttpContext.Current.ApplicationInstance.CompleteRequest();
应该强制请求结束并写出那一块。我将其添加为我的 finally 块中的最后一行,使用调试器我知道它是 运行ning。但是,该块仍未设置。
请记住,所有这些都适用于我托管在 IIS 中的开发机器,但不适用于 Web 服务器。
我的机器是 运行ning Windows 10,我安装了 asp.net 5,IIS7 全部默认。
Web 服务器是 Windows 服务器(我不确定版本)运行 IIS8 但 它只安装了 asp.net 4.5。 我最初的预感是这就是问题所在 - asp.net 框架的不同版本,但我检查了一下,该项目仅针对 ASP.NET 4.5。我仍然会尝试更新服务器,但由于我的目标是 4.5,我认为它不会有任何好处。
我认为问题是由于 .net 框架中的一个错误。将服务器更新到 .net 4.6 后,它正在运行。
这是我应用的补丁。 https://www.microsoft.com/en-us/download/details.aspx?id=48137