繁体   English   中英

HttpListener:请求的地址在此上下文中无效

[英]HttpListener: The requested address is not valid in this context

使用时创建HttpListener对象时

var server = new HttpListener();
server.Prefixes.Add("http://*:8080/");
server.Start();

一切正常。 但是,当我使用时

var server = new HttpListener();
server.Prefixes.Add("http://demindiro.com:8080/");
server.Start();

它抛出System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context (下面的完整堆栈跟踪)。

在谷歌搜索异常后,很明显它与使用的地址有关。 在挖掘了HttpListenerEndPointManagerMono源代码后,我确定问题可能在这段代码中(在GetEPListener处 ):

static EndPointListener GetEPListener (string host, int port, HttpListener listener, bool secure)
{
    IPAddress addr;
    if (host == "*")
        addr = IPAddress.Any;
    else if (IPAddress.TryParse(host, out addr) == false){
        try {
#pragma warning disable 618
            IPHostEntry iphost = Dns.GetHostByName(host);
#pragma warning restore 618
            if (iphost != null)
                addr = iphost.AddressList[0]; // <---
            else
                addr = IPAddress.Any;
        } catch {
            addr = IPAddress.Any;
        }
        // ...    
    }

在我看来,在几乎所有情况下都使用ÌPAddress.Any ,除非它可以将外部IP地址与给定的主机名相关联。

但是,我只能假设这是故意的,因为它是相当明确的编写,似乎HttpListener对其他开发人员来说效果很好,所以在这一点上,我对这里出了什么问题一无所知。


堆栈跟踪:

 Unhandled Exception: System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context at System.Net.Sockets.Socket.Bind (System.Net.EndPoint localEP) [0x00043] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointListener..ctor (System.Net.HttpListener listener, System.Net.IPAddress addr, System.Int32 port, System.Boolean secure) [0x00047] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.GetEPListener (System.String host, System.Int32 port, System.Net.HttpListener listener, System.Boolean secure) [0x0009d] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.AddPrefixInternal (System.String p, System.Net.HttpListener listener) [0x0005e] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.AddListener (System.Net.HttpListener listener) [0x0009c] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.HttpListener.Start () [0x0000f] in <50d80b08c1a5449282b22aedf03ce925>:0 at Playground.Playground.StartHttpListener () [0x00016] in <d51cc6c047ee47c9a05c5e174876cbec>:0 at Playground.Playground.Main (System.String[] args) [0x00000] in <d51cc6c047ee47c9a05c5e174876cbec>:0 [ERROR] FATAL UNHANDLED EXCEPTION: System.Net.Sockets.SocketException (0x80004005): The requested address is not valid in this context at System.Net.Sockets.Socket.Bind (System.Net.EndPoint localEP) [0x00043] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointListener..ctor (System.Net.HttpListener listener, System.Net.IPAddress addr, System.Int32 port, System.Boolean secure) [0x00047] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.GetEPListener (System.String host, System.Int32 port, System.Net.HttpListener listener, System.Boolean secure) [0x0009d] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.AddPrefixInternal (System.String p, System.Net.HttpListener listener) [0x0005e] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.EndPointManager.AddListener (System.Net.HttpListener listener) [0x0009c] in <50d80b08c1a5449282b22aedf03ce925>:0 at System.Net.HttpListener.Start () [0x0000f] in <50d80b08c1a5449282b22aedf03ce925>:0 at Playground.Playground.StartHttpListener () [0x00016] in <d51cc6c047ee47c9a05c5e174876cbec>:0 at Playground.Playground.Main (System.String[] args) [0x00000] in <d51cc6c047ee47c9a05c5e174876cbec>:0 

PS:我明确想要使用demindiro.com ,而不是*

PS2:我知道有很多关于这个特殊异常的主题,但似乎没有人关注HttpListener.Start()抛出这个异常。


更新我再次搜索我的问题,但这一次我专门寻找TcpListener ,我找到了这个答案: httpsTcpListener

TcpListener只能绑定到运行它的计算机的本地IP地址。 因此,您指定的IP不是本地计算机的IP。 您的公共IP与本地计算机的IP不同,尤其是在使用某种NAT时。

如果我没记错的话,通常只需要IPAddress.Any作为你的IP来初始化监听器。

因此,如果我直截了当,这意味着要绑定到我的外部IP,我不得不离开这个

Server <-- external IP --> The Internet

对此

 Server <-- external IP --> The Internet 

它是否正确? 如果是,如何在没有调制解调器的情况下将服务器连接到互联网? 或者我该怎么办?

考虑到http://demindiro.com似乎有效,这可能是多余的。 然而...

这里有一些你需要注意的事情:

通过主机名绑定是不好的

通常,主机名绑定是A Bad Idea™。 如果主机名解析为多个地址 - 例如,当您启用IPv6时为localhost ,或者在同一台计算机上有多个IPv4地址等时 - 您无法控制它绑定到哪个地址。 localhost的情况下,您可能(有时令人不愉快)惊讶地发现它绑定到::1但不是127.0.0.1

但问题是demindiro.com是一个不能解析为计算机上存在的内部IP地址的名称,因此您无法直接绑定它。 确切地说, demindiro.com是一个主机名,它通过DNS解析为IPv4地址109.132.169.132 ,这几乎可以肯定不是您的计算机所具有的IP地址。

绑定到主机名不会绑定到主机名

相反,它绑定到主机名在计算机上解析的第一个IP地址。 不多也不少。

假设您修改主机文件以使demindiro.com解析(仅在您的计算机上)为有效的本地IPv4地址,如192.168.0.123 如果我在您的网络上并且我连接到该地址,我会得到您的服务。 无论我想要访问哪个主机名,只要它解析为该地址我将获得连接。 我可以通过IP或任何解析到该IP的主机名直接连接,你永远不会知道。

只有在建立连接并且解析了请求之后,您才能(有时)检测哪个主机名是请求的目标。

在HTTP(S)请求的情况下,主机名通常作为请求的一部分发送。 这允许Web服务器在同一地址上托管多个站点。 Web服务器接收请求,确定应在本地站点之间路由的位置,然后将请求发送到已解析的站点。 在HTTPS中做起来有点困难,但这个问题主要是由SNI解决的。

但是,如果我使用没有主机名的绝对路径向您的服务发出HTTP 1.0请求,那么什么?

绑定只能是本地的

换句话说,您无法绑定到运行代码的计算机上不存在的地址。 更重要的是,除非直接在您的计算机上终止,否则您无法直接绑定到Internet连接的公共地址。 由于我们中没有多少人在我们的桌面上运行拨号或桥接PPPoE,因此您尝试绑定的地址可能无法使用。

通常,您需要做的是在Internet连接上设置某种NAT重定向,以将端口8080上的传入请求转换为公共地址109.132.169.132 ,并将它们重定向到您的内部地址( 192.168.0.123或其他)。

这是假设demindiro.com解析为您的公共IP地址。 如果没有,那么你就会遇到各种各样的问题。 跨公共网络的NAT是......很奇怪。 您在链中添加的每个NAT都会增加更多复杂性,更多故障点等。

您可以将您的网络可视化如下

local machine -> local interface 
              -> interface 1 (Private IP) <-> External IP
              -> interface 2 (Private IP2) <-> External IP

现在,在大多数情况下,云中的VM将具有1个网络接口和1个本地接口。 本地接口是环回接口, interface 1将具有Private IP

现在,当您在代码中使用以下内容时

server.Prefixes.Add("http://*:8080/");

它意味着绑定到您机器上的所有接口。 这意味着如果你这样做

curl http://localhost:8080

要么

curl http://<privateIP>:8080

两者都有效。

现在,如果您将example.com映射到下面的externalIP运行命令,它也可以正常工作

curl http://example.com:8080

这是因为您的example.com映射到<extrenalIP> ,它将所有流量转发到<privateIP> 现在当你在下面使用时。

server.Prefixes.Add("http://example.com:8080/");

它转化为

server.Prefixes.Add("http://<externalIP>:8080/");

并且这不是上下文中的有效IP,只有机器的127.0.0.1<PrivateIP>有效。 因此,如果您有多个接口,那么只有您应该担心不使用* 现在,如果你有多个接口和一个特定的界面来监听你的域,那么你将在/etc/hosts文件中输入相同的内容,如下所示

<PrivateIP> example.com

然后你可以使用

server.Prefixes.Add("http://example.com:8080/");

现在,如果您担心有人可以使用不同的主机名指向您的计算机,并且您不希望服务器在这种情况下做出响应。 然后,您必须检查代码中的Host标头,并根据相同的方法拒绝请求。 或者您可以使用像nginx这样的网络服务器,只响应针对example.com请求

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM