[英]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
(下面的完整堆栈跟踪)。
在谷歌搜索异常后,很明显它与使用的地址有关。 在挖掘了HttpListener和EndPointManager的Mono源代码后,我确定问题可能在这段代码中(在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
,我找到了这个答案: https : TcpListener
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.