Kestrel的ListenAnyIP和ListenLocalhost的区别
问题
在上篇文章,把AAStore.ProductCatalog.Api部署到docker中运行,输入地址访问报错如下图,说明外部无法访问这个url。(当然本地开发环境测试是可以访问的)。后来修改此处options.ListenLocalhost(8081)的代码改成options.ListenAnyIP(8081),可以访问了。那这两种写法有什么区别呢?
在区别之前,我们先熟悉几个概念(如果网络知识比较好的,可以跳过):
本地回环地址(Loopback Address):
百度定义的定义,127.0.0.1,通常被称为本地回环地址(Loopback Address),不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。在Windows操作系统中也有相似的定义,所以通常在安装网卡前就可以ping通这个本地回环地址。一般都会用来检查本地网络协议、基本数据接口等是否正常的。
IPv6的本地回环地址形式:0:0:0:0:0:0:0:1,同IPV4中127.0.0.1地址的含义一样,表示节点自已,也可以是::1,大多数windows和linux电脑上都将localhost指向了127.0.0.1这个地址,相当于是本机地址。
ip地址类型
公有地址
公有地址(Public address)由Inter NIC(Internet Network Information Center因特网信息中心)负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。
私有地址
私有地址(Private address)属于非注册地址,专门为组织机构内部使用。以下列出留用的内部私有地址
- A类 10.0.0.0--10.255.255.255
- B类 172.16.0.0--172.31.255.255
- C类 192.168.0.0--192.168.255.255
IPv6 [::] ( 0000:0000:0000:0000:0000:0000:0000:0000的简写), IPv4 0.0.0.0 含义:
维基百科解释,表示无效的,未知,不可用的目标
在服务器中,常常表示监听本机所有的ip地址。一般我们在服务端绑定端口的时候可以选择绑定到0.0.0.0,这样就可以通过多个ip地址访问我的服务。
ListenLocalhost 和ListenAnyIP 区别
通过编码配置Kestrel监听端口有三个方法可以实现ListenLocalhost、ListenAnyIP、Listen,其中ListenLocalhost等同于Listen的IPAddress.IPv6Loopback 和IPAddress.Loopback,ListenAnyIP等同于Listen的IPAddress.IPv6Any和IPAddress.Any。下面我看看可以查看他们的源代码。
ListenLocalhost、ListenAnyIP 两个方法的源码
/// <summary>
/// Listens on ::1 and 127.0.0.1 with the given port. Requesting a dynamic port by specifying 0 is not supported
/// for this type of endpoint.
/// </summary>
public void ListenLocalhost(int port, Action<ListenOptions> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new LocalhostListenOptions(port);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
/// <summary>
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
/// </summary>
public void ListenAnyIP(int port, Action<ListenOptions> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new AnyIPListenOptions(port);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
通过源码我们可以发现,他们之间的区别是在构造listenopthons对象不同,分别使用LocalhostListenOptions和AnyIPListenOptions进行new创建实例,而AnyIPListenOptions和LocalhostListenOptions都继承类ListenOptions,并且重写BindAsync方法。源码如下:
internal sealed class LocalhostListenOptions : ListenOptions
{
internal LocalhostListenOptions(int port)
: base(new IPEndPoint(IPAddress.Loopback, port))
{
if (port == 0)
{
throw new InvalidOperationException(CoreStrings.DynamicPortOnLocalhostNotSupported);
}
}
//绑定回环地址ipv4是127.0.0.1 ,iPV6是::1
internal override async Task BindAsync(AddressBindContext context)
{
var exceptions = new List<Exception>();
try
{
var v4Options = Clone(IPAddress.Loopback);
await AddressBinder.BindEndpointAsync(v4Options, context).ConfigureAwait(false);
}
catch (Exception ex) when (!(ex is IOException))
{
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, GetDisplayName(), "IPv4 loopback", ex.Message);
exceptions.Add(ex);
}
try
{
var v6Options = Clone(IPAddress.IPv6Loopback);
await AddressBinder.BindEndpointAsync(v6Options, context).ConfigureAwait(false);
}
catch (Exception ex) when (!(ex is IOException))
{
context.Logger.LogWarning(0, CoreStrings.NetworkInterfaceBindingFailed, GetDisplayName(), "IPv6 loopback", ex.Message);
exceptions.Add(ex);
}
if (exceptions.Count == 2)
{
throw new IOException(CoreStrings.FormatAddressBindingFailed(GetDisplayName()), new AggregateException(exceptions));
}
// If StartLocalhost doesn't throw, there is at least one listener.
// The port cannot change for "localhost".
context.Addresses.Add(GetDisplayName());
}
}
internal sealed class AnyIPListenOptions : ListenOptions
{
internal AnyIPListenOptions(int port)
: base(new IPEndPoint(IPAddress.IPv6Any, port))
{
}
//如果本机不支持 IPv6就绑定ipv4
internal override async Task BindAsync(AddressBindContext context)
{
// when address is 'http://hostname:port', 'http://*:port', or 'http://+:port'
try
{
await base.BindAsync(context).ConfigureAwait(false);
}
catch (Exception ex) when (!(ex is IOException))
{
context.Logger.LogDebug(CoreStrings.FormatFallbackToIPv4Any(IPEndPoint.Port));
// for machines that do not support IPv6
EndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.Port);
await base.BindAsync(context).ConfigureAwait(false);
}
}
}
小结:通过以上分析,端口绑定时,建议使用IPAddress.Any,可以支持ipv6和ipv4地址。
webBuilder.ConfigureKestrel(options =>
{
//1.ListenLocalhost方法
//options.ListenLocalhost(8081);
//2.ListenAnyIP方法
options.ListenAnyIP(8081);
//3.Listen方法
// options.Listen(IPAddress.Loopback, 8081);
// Setup a HTTP/2 endpoint without TLS.
options.ListenAnyIP(18081, o => o.Protocols =
HttpProtocols.Http1AndHttp2);
});
参考:https://juejin.im/post/5d258b6ae51d454f73356dcf
- 算法模板——线段树1(区间加法+区间求和)
- 【LeetCode 205】关关的刷题日记38 Isomorphic Strings
- JavaScript基础2---控制权DOM操作
- 算法模板——线段树3(区间覆盖值+区间求和)
- 算法模板——线段树4(区间加+区间乘+区间覆盖值+区间求和)
- 【LeetCode 204】关关的刷题日记39 Count Primes
- 算法模板——并查集 1
- Java 持久化操作之 --io流与序列化
- 算法模板——LCA(最近公共祖先)
- 算法模板——AC自动机
- UOJ #117. 欧拉回路
- 算法模板——左偏树(可并堆)
- 算法模板——二分图匹配
- Codevs2776 寻找代表元
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法