我很难确定在使用 .NET 的 HttpWebRequest 类调用远程服务器(特别是 REST Web 服务)时是否有办法处理潜在的连接问题。根据我的调查,WebClient 类的行为是相同的,这在某种程度上是意料之中的,因为它似乎只为 HttpWebRequest 提供了一个更简单的接口(interface)。
出于模拟目的,我编写了一个非常简单的 HTTP 服务器,它的行为不符合 HTTP 1.1 RFC。它所做的是接受客户端连接,然后发送适当的 HTTP 1.1 header 和“Hello World!”有效负载返回给客户端并关闭套接字,服务器端接受客户端连接的线程如下所示:
private const string m_defaultResponse = "<html><body><h1>Hello World!</h1></body></html>";
private void Listen()
{
while (true)
{
using (TcpClient clientConnection = m_listener.AcceptTcpClient())
{
NetworkStream stream = clientConnection.GetStream();
StringBuilder httpData = new StringBuilder("HTTP/1.1 200 OK\r\nServer: ivy\r\nContent-Type: text/html\r\n");
httpData.AppendFormat("Content-Length: {0}\r\n\r\n", m_defaultResponse.Length);
httpData.AppendFormat(m_defaultResponse);
Thread.Sleep(3000); // Sleep to simulate latency
stream.Write(Encoding.ASCII.GetBytes(httpData.ToString()), 0, httpData.Length);
stream.Close();
clientConnection.Close();
}
}
}
由于 HTTP 1.1 RFC 指出 HTTP 1.1 默认情况下会保持连接处于事件状态,并且如果服务器想要关闭连接,则它必须发送“连接:关闭”响应 header ,这对于客户端来说是意外的行为。客户端通过以下方式使用 HttpWebRequest:
private static void SendRequest(object _state)
{
WebResponse resp = null;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.0.32:7070/asdasd");
request.Timeout = 50 * 1000;
DateTime requestStart = DateTime.Now;
resp = request.GetResponse();
TimeSpan requestDuration = DateTime.Now - requestStart;
Console.WriteLine("OK. Request took: " + (int)requestDuration.TotalMilliseconds + " ms.");
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
{
Console.WriteLine("Timeout occurred");
}
else
{
Console.WriteLine(ex);
}
}
finally
{
if (resp != null)
{
resp.Close();
}
((ManualResetEvent)_state).Set();
}
}
上述方法是通过ThreadPool.QueueUserWorkItem(waitCallback, stateObject)进行排队的。 ManualResetEvent 用于控制排队行为,这样整个线程池就不会被等待任务填满(因为 HttpWebRequest 隐式使用工作线程,因为它在内部异步运行以实现超时功能)。
所有这一切的问题在于,一旦 HttpWebRequest 的底层 ServicePoint 的所有连接都“用完”(即被远程服务器关闭),将不会有新的连接打开。 ServicePoint 的 ConnectionLeaseTimeout 是否设置为较低的值(10 秒)也无关紧要。一旦系统进入此状态,它将不再正常运行,因为它不会自动重新连接并且所有后续的 HttpWebRequests 都会超时。现在真正的问题是,是否有一种方法可以通过在某些条件下以某种方式破坏 ServicePoint 或关闭底层连接来解决这个问题(我对 ServicePoint.CloseConnectionGroup() 还没有任何运气,该方法在如何使用方面也没有记录正确使用它)。
有人知道我该如何解决这个问题吗?
最佳答案
我根据这里的一些想法提出的解决方案是自己管理连接。如果将唯一的 ConnectionGroupName 分配给 WebRequest(例如 Guid.NewGuid().ToString()),将在 ServicePoint 中为该请求创建一个具有一个连接的新连接组。请注意,此时不再有连接限制,因为 .NET 限制每个连接组而不是每个 ServicePoint,因此您必须自己处理。您需要重用连接组,以便重用与 KeepAlive 的现有连接,但如果发生 WebException 异常,则应销毁请求的连接组,因为它可能已过时。像这样(为每个主机名创建一个新实例):
public class ConnectionManager {
private const int _maxConnections = 4;
private Semaphore _semaphore = new Semaphore(_maxConnections, _maxConnections);
private Stack<string> _groupNames = new Stack<string>();
public string ObtainConnectionGroupName() {
_semaphore.WaitOne();
return GetConnectionGroupName();
}
public void ReleaseConnectionGroupName(string name) {
lock (_groupNames) {
_groupNames.Push(name);
}
_semaphore.Release();
}
public string SwapForFreshConnection(string name, Uri uri) {
ServicePoint servicePoint = ServicePointManager.FindServicePoint(uri);
servicePoint.CloseConnectionGroup(name);
return GetConnectionGroupName();
}
private string GetConnectionGroupName() {
lock (_groupNames) {
return _groupNames.Count != 0 ? _groupNames.Pop() : Guid.NewGuid().ToString();
}
}
}
关于c# - HttpWebRequest 如何处理(过早)关闭底层 TCP 连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1390780/
我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
下面的代码在我第一次运行它时就可以正常工作:require'rubygems'require'spreadsheet'book=Spreadsheet.open'/Users/me/myruby/Mywks.xls'sheet=book.worksheet0row=sheet.row(1)putsrow[1]book.write'/Users/me/myruby/Mywks.xls'当我再次运行它时,我会收到更多消息,例如:/Library/Ruby/Gems/1.8/gems/spreadsheet-0.6.5.9/lib/spreadsheet/excel/reader.rb:11
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?
require"socket"server="irc.rizon.net"port="6667"nick="RubyIRCBot"channel="#0x40"s=TCPSocket.open(server,port)s.print("USERTesting",0)s.print("NICK#{nick}",0)s.print("JOIN#{channel}",0)这个IRC机器人没有连接到IRC服务器,我做错了什么? 最佳答案 失败并显示此消息::irc.shakeababy.net461*USER:Notenoughparame
假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案