使用创建HttpListener对象时
var server = new HttpListener();
server.Prefixes.Add("http://*:8080/");
server.Start();
Run Code Online (Sandbox Code Playgroud)
一切正常。但是,当我使用
var server = new HttpListener();
server.Prefixes.Add("http://demindiro.com:8080/");
server.Start();
Run Code Online (Sandbox Code Playgroud)
它抛出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;
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
在我看来,几乎所有情况Ì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
Run Code Online (Sandbox Code Playgroud)
PS:我明确想使用demindiro.com,而不是*。
PS2:我知道有很多关于此特定异常的主题,但似乎都没有关于HttpListener.Start()抛出该异常的问题。
更新
我再次用谷歌搜索了这个问题,但是这次我专门TcpListener寻找了这个问题,我找到了这个答案:https : //stackoverflow.com/a/17092670/7327379
TcpListener只能绑定到运行它的计算机的本地IP地址。因此,您指定的IP不是本地计算机的IP。您的公共IP与本地计算机的IP不同,尤其是在使用某种NAT的情况下。
如果我没记错的话,通常只需执行IPAddress.Any作为您的IP即可初始化侦听器。
因此,如果我明白了这一点,这意味着要绑定到我的外部IP,我一定会从此开始
Server <-- local IP --> Modem <-- external IP --> The Internet
Run Code Online (Sandbox Code Playgroud)
对此
Server <-- external IP --> The Internet
Run Code Online (Sandbox Code Playgroud)
它是否正确?如果是,如何在不使用调制解调器的情况下将服务器连接到Internet?还是我该怎么办?
考虑到http://demindiro.com似乎可以工作,这可能是多余的。然而...
\n\n您需要注意以下几点:
\n\n一般来说,按主机名绑定是 A Bad Idea\xe2\x84\xa2。如果主机名解析为多个地址(localhost例如,当您启用了 IPv6 时,或者当您在同一台计算机上有多个 IPv4 地址时等),您将无法控制它绑定到哪个地址。在这种情况下,localhost您可能会(有时令人不快地)惊讶地发现它绑定::1但不绑定127.0.0.1。
但这里的问题是该demindiro.com名称无法解析为计算机上存在的内部 IP 地址,因此您无法直接绑定到它。准确地说,demindiro.com它是(通过 DNS)解析为 IPv4 地址的主机名,109.132.169.132而该地址(几乎可以肯定)不是您的计算机拥有的 IP 地址。
相反,它绑定到主机名在计算机上解析的第一个 IP 地址。不多也不少。
\n\n假设您修改主机文件以demindiro.com解析(仅在您的计算机上)有效的本地 IPv4 地址,例如192.168.0.123. 如果我在您的网络上并且连接到该地址,我就可以获得您的服务。我尝试访问的主机名并不重要,只要它解析为该地址,我就能获得连接。我可以通过 IP 或解析为该 IP 的任何主机名直接连接,而您永远不会知道。
只有在建立连接并且解析请求之后,您才能(有时)检测哪个主机名是请求的目标。
\n\n对于 HTTP(S) 请求,主机名通常作为请求的一部分发送。这允许网络服务器在同一地址上托管多个站点。Web 服务器接收请求,找出应在本地站点中路由的位置,然后将请求发送到已解析的站点。在 HTTPS 中做到这一点有点困难,但这个问题主要由SNI解决。
\n\n但是,如果我使用没有主机名的绝对路径向您的服务发出 HTTP 1.0 请求...然后呢?
\n\n换句话说,您无法绑定到运行代码的计算机上不存在的地址。更重要的是,您不能直接绑定到互联网连接的公共地址,除非它直接在您的计算机上终止。由于我们中没有多少人运行拨号或桥接 PPPoE 到我们的桌面,因此您尝试绑定的地址可能不可用。
\n\n一般来说,您需要做的是在您的互联网连接上设置某种 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
Run Code Online (Sandbox Code Playgroud)
现在,在大多数情况下,云中的虚拟机将有 1 个网络接口和 1 个本地接口。本地接口是环回接口,并且interface 1会有一个Private IP.
现在,当您在代码中使用以下内容时
server.Prefixes.Add("http://*:8080/");
Run Code Online (Sandbox Code Playgroud)
这意味着绑定到您机器上的所有接口。这意味着如果你这样做
curl http://localhost:8080
Run Code Online (Sandbox Code Playgroud)
或者
curl http://<privateIP>:8080
Run Code Online (Sandbox Code Playgroud)
两者都可以。
现在,如果您映射example.com到externalIP下面的运行命令,它也会起作用
curl http://example.com:8080
Run Code Online (Sandbox Code Playgroud)
这是因为您的example.com映射到<extrenalIP>,它将把所有流量转发到<privateIP>。现在,当您在 . 代码
server.Prefixes.Add("http://example.com:8080/");
Run Code Online (Sandbox Code Playgroud)
它翻译成
server.Prefixes.Add("http://<externalIP>:8080/");
Run Code Online (Sandbox Code Playgroud)
这在上下文中不是有效的 IP,只有机器的127.0.0.1和是有效的。<PrivateIP>因此,如果您有多个接口,那么只有您应该担心不使用*. 现在,如果您有多个接口并且有一个特定的接口来侦听您的域,那么您将在文件中进行相同的条目,/etc/hosts如下所示
<PrivateIP> example.com
Run Code Online (Sandbox Code Playgroud)
然后你可以使用
server.Prefixes.Add("http://example.com:8080/");
Run Code Online (Sandbox Code Playgroud)
现在,如果您担心有人可以使用不同的主机名指向您的计算机,并且您不希望服务器在这种情况下做出响应。然后,您必须检查Host代码中的标头并根据相同的标头拒绝请求。或者您可以使用类似的网络服务器nginx,并且仅响应针对的请求example.com
| 归档时间: |
|
| 查看次数: |
1093 次 |
| 最近记录: |