简体   繁体   English

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

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

When creating an HttpListener object using 使用时创建HttpListener对象时

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

everything works fine. 一切正常。 However, when I use 但是,当我使用时

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

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

After googling the exception, it was rather clear it had something to do with the address used. 在谷歌搜索异常后,很明显它与使用的地址有关。 After digging in the Mono source code for HttpListener and EndPointManager I determined the issue probably lies within this section of code (at GetEPListener ): 在挖掘了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;
        }
        // ...    
    }

It occurs to me that in almost all cases ÌPAddress.Any is used except when it can associate an external IP address with the given host name. 在我看来,在几乎所有情况下都使用ÌPAddress.Any ,除非它可以将外部IP地址与给定的主机名相关联。

However, I can only assume this was intentional because it is rather explicitly written and it seems HttpListener works just fine for other developers, so at this point, I'm clueless as to what is going wrong here. 但是,我只能假设这是故意的,因为它是相当明确的编写,似乎HttpListener对其他开发人员来说效果很好,所以在这一点上,我对这里出了什么问题一无所知。


Stacktrace: 堆栈跟踪:

 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: I explicitly want to use demindiro.com , not * . PS:我明确想要使用demindiro.com ,而不是*

PS2: I know there are many topics concerning this particular exception, but none seem to concern HttpListener.Start() throwing this exception. PS2:我知道有很多关于这个特殊异常的主题,但似乎没有人关注HttpListener.Start()抛出这个异常。


UPDATE I've googled my issue again but this time I looked specifically for TcpListener and I found this answer: https://stackoverflow.com/a/17092670/7327379 更新我再次搜索我的问题,但这一次我专门寻找TcpListener ,我找到了这个答案: httpsTcpListener

The TcpListener can only be bound to a local IP Address of the computer that runs it. TcpListener只能绑定到运行它的计算机的本地IP地址。 So the IP you're specifying isn't an IP of the local machine. 因此,您指定的IP不是本地计算机的IP。 Your public IP isn't the same IP as your local machine, especially if you're using some kind of NAT. 您的公共IP与本地计算机的IP不同,尤其是在使用某种NAT时。

If I recall correctly, it's common to just do IPAddress.Any as your IP to initialise the listener. 如果我没记错的话,通常只需要IPAddress.Any作为你的IP来初始化监听器。

So if I get this straight, this means that to bind to my external IP, I somehow have to go from this 因此,如果我直截了当,这意味着要绑定到我的外部IP,我不得不离开这个

Server <-- external IP --> The Internet

to this 对此

 Server <-- external IP --> The Internet 

Is this correct? 它是否正确? If yes, how do you connect a server to the internet without a modem? 如果是,如何在没有调制解调器的情况下将服务器连接到互联网? Or what else should I do? 或者我该怎么办?

Considering that http://demindiro.com appears to work, this might be redundant. 考虑到http://demindiro.com似乎有效,这可能是多余的。 However... 然而...

There are a few things here that you need to be aware of: 这里有一些你需要注意的事情:

Binding via host name is bad 通过主机名绑定是不好的

In general binding by host name is A Bad Idea™. 通常,主机名绑定是A Bad Idea™。 If the host name resolves to multiple addresses - localhost when you have IPv6 enabled for instance, or when you have multiple IPv4 addresses on the same machine, etc - you don't get to control which of those addresses it binds to. 如果主机名解析为多个地址 - 例如,当您启用IPv6时为localhost ,或者在同一台计算机上有多个IPv4地址等时 - 您无法控制它绑定到哪个地址。 In the case of localhost you can be (sometimes unpleasantly) surprised to find that it binds to ::1 but not 127.0.0.1 . localhost的情况下,您可能(有时令人不愉快)惊讶地发现它绑定到::1但不是127.0.0.1

The problem here however is that demindiro.com is a name that doesn't resolve to an internal IP address that is present on the computer, so you can't bind to it directly. 但问题是demindiro.com是一个不能解析为计算机上存在的内部IP地址的名称,因此您无法直接绑定它。 To be precise, demindiro.com is a host name that resolves (via DNS) to the IPv4 address 109.132.169.132 which is (almost certainly) not an IP address that your computer has. 确切地说, demindiro.com是一个主机名,它通过DNS解析为IPv4地址109.132.169.132 ,这几乎可以肯定不是您的计算机所具有的IP地址。

Binding to a host name doesn't bind to a host name 绑定到主机名不会绑定到主机名

Instead it binds to the first IP address that the host name resolves to on the machine. 相反,它绑定到主机名在计算机上解析的第一个IP地址。 No more, no less. 不多也不少。

Let's say you modify your host file to have demindiro.com resolve (on your machine only) to a valid local IPv4 address, like 192.168.0.123 . 假设您修改主机文件以使demindiro.com解析(仅在您的计算机上)为有效的本地IPv4地址,如192.168.0.123 If I am on your network and I make a connection to that address, I get your service. 如果我在您的网络上并且我连接到该地址,我会得到您的服务。 Doesn't matter what host name I am trying to get to, as long as it resolves to that address I'm going to get a connection. 无论我想要访问哪个主机名,只要它解析为该地址我将获得连接。 I could connect directly via IP, or any host name that resolves to that IP, and you will never know. 我可以通过IP或任何解析到该IP的主机名直接连接,你永远不会知道。

It's only after a connection has been made and the request has been parsed that you can (sometimes) detect which host name was the target of the request. 只有在建立连接并且解析了请求之后,您才能(有时)检测哪个主机名是请求的目标。

In the case of HTTP(S) requests the host name is generally sent as a part of the request. 在HTTP(S)请求的情况下,主机名通常作为请求的一部分发送。 This allows web servers to host more than a single site on the same address. 这允许Web服务器在同一地址上托管多个站点。 The web server receives the request, figures out where it should be routed among the local sites, then sends the request on to the resolved site. Web服务器接收请求,确定应在本地站点之间路由的位置,然后将请求发送到已解析的站点。 It has been a bit more difficult to do in HTTPS, but that problem is mostly solved by SNI . 在HTTPS中做起来有点困难,但这个问题主要是由SNI解决的。

But if I make an HTTP 1.0 request to your service using an absolute path with no hostname... then what? 但是,如果我使用没有主机名的绝对路径向您的服务发出HTTP 1.0请求,那么什么?

Bindings can only be local 绑定只能是本地的

In other words, you can't bind to an address that isn't present on the machine running your code. 换句话说,您无法绑定到运行代码的计算机上不存在的地址。 More to the point, you can't directly bind to the public address of your internet connection unless it terminates directly on your machine. 更重要的是,除非直接在您的计算机上终止,否则您无法直接绑定到Internet连接的公共地址。 Since not many of us are running dial-up or bridging PPPoE to our desktops, the address you're trying to bind to is probably not available. 由于我们中没有多少人在我们的桌面上运行拨号或桥接PPPoE,因此您尝试绑定的地址可能无法使用。

In general what you need to do is set up some sort of NAT redirect on your internet connection to translate incoming requests on port 8080 to the public address 109.132.169.132 and redirect them to your internal address ( 192.168.0.123 or whatever). 通常,您需要做的是在Internet连接上设置某种NAT重定向,以将端口8080上的传入请求转换为公共地址109.132.169.132 ,并将它们重定向到您的内部地址( 192.168.0.123或其他)。

That's assuming that demindiro.com resolves to your public IP address. 这是假设demindiro.com解析为您的公共IP地址。 If not then you've got all sorts of other issues. 如果没有,那么你就会遇到各种各样的问题。 NAT across a public network is... weird. 跨公共网络的NAT是......很奇怪。 Every NAT you add in the chain adds more complexity, more points of failure, etc. 您在链中添加的每个NAT都会增加更多复杂性,更多故障点等。

You can visualise your network something like below 您可以将您的网络可视化如下

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

Now in most cases your VM in the cloud will have 1 network interface and one local interface. 现在,在大多数情况下,云中的VM将具有1个网络接口和1个本地接口。 Local interfaces is the loopback interface and the interface 1 will have a Private IP . 本地接口是环回接口, interface 1将具有Private IP

Now when you use below in your code 现在,当您在代码中使用以下内容时

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

It means bind to all interfaces on your machine. 它意味着绑定到您机器上的所有接口。 Which means if you do 这意味着如果你这样做

curl http://localhost:8080

or 要么

curl http://<privateIP>:8080

Both would work. 两者都有效。

Now if you map example.com to the externalIP run below command it will work as well 现在,如果您将example.com映射到下面的externalIP运行命令,它也可以正常工作

curl http://example.com:8080

This is because your example.com maps to <extrenalIP> , which will forward all traffic to the <privateIP> . 这是因为您的example.com映射到<extrenalIP> ,它将所有流量转发到<privateIP> Now when you use below in your . 现在当你在下面使用时。 code

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

It translate to 它转化为

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

And this not a valid IP in the context, only the 127.0.0.1 and the <PrivateIP> of the machine are valid. 并且这不是上下文中的有效IP,只有机器的127.0.0.1<PrivateIP>有效。 So if you have multiple interfaces then only you should worry about not using * . 因此,如果您有多个接口,那么只有您应该担心不使用* Now if you have multiple interfaces and your a specific interface to listen for your domain then you will make a entry of the same in your /etc/hosts file like below 现在,如果你有多个接口和一个特定的界面来监听你的域,那么你将在/etc/hosts文件中输入相同的内容,如下所示

<PrivateIP> example.com

And then you can use 然后你可以使用

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

Now if your concern is that someone can point to your machine with a different host name and you don't want to server to respond in such a case. 现在,如果您担心有人可以使用不同的主机名指向您的计算机,并且您不希望服务器在这种情况下做出响应。 Then you will have to check for the Host header in your code and reject the request based on the same. 然后,您必须检查代码中的Host标头,并根据相同的方法拒绝请求。 Or you can use a webserver like nginx and only respond to request directed towards example.com 或者您可以使用像nginx这样的网络服务器,只响应针对example.com请求

暂无
暂无

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

相关问题 SocketException:请求的地址在上下文中无效 - SocketException: The requested address is not valid in the context “请求的地址在其上下文中无效” SocketException - “The requested address is not valid in its context” SocketException 套接字错误请求的地址在其上下文中无效 - Socket Error The requested address is not valid in its context WCF Tcp绑定“请求的地址在其上下文中无效”错误 - WCF Tcp Binding “The requested address is not valid in its context” Error 当我尝试侦听端口时,请求的地址在其上下文中无效 - The requested address is not valid in its context when I try to listen a port 获取请求的地址在其上下文中无效。 在我的聊天应用程序中 - Getting The requested address is not valid in its context. in my chat application 为什么我的服务器 TcpListener 使用本地地址而不使用公共地址? - 例外:请求的地址在其上下文中无效 - Why is my Server TcpListener Working with Local Address but not with Public Address? - Exception: The requested address is not valid in its context Socket.bind()在Win7上返回“请求的地址在其上下文中无效。” - Socket.bind() returns “The requested address is not valid in its context.” on Win7 32feet .NET api拒绝连接“请求的地址在其上下文中无效” - 32feet .NET api refusing to connect “The requested address is not valid in its context” c#TCP / IP通信错误:请求的地址在其自身上下文中无效 - c# TCP/IP communication error: Address requested non valid in own context
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM