Bonjour 服务

文章转载自http://blog.youkuaiyun.com/keyboardota/article/details/11125073

  1. Bonjour简介
    Bonjour是Apple推出的零配置网络协议,主要的目的是在缺少中心服务器的情况下解决网络设备的IP获取,名称解析和服务发现等关键问题。
    Bonjour这个词来源于法语,是“你好”的意思,应该是指遵从这个协议的设备可以通过主动打招呼的形式发现彼此。Bonjour的法语发音大概为“甭油喝”,我们不会读法语,也不会延用法语发音,Bonjour的英语发音大概是“帮就而”,重音在第一个音节。

  2. Bonjour可以做什么
    如上面提到的,Bonjour可以完成的工作主要是在缺少中心服务器的情况下解决IP获取,名称解析和服务发现这三个问题。

IP获取
在传统网络环境下,设备的IP地址通过两种方式获取,一种是静态配置,通过手工方式为设备指定一个IP地址,一种是动态配置,设备通过路由器的DHCP服务获得动态的IP地址。在无中心服务器的网络环境下,没有中心服务器提供DHCP服务,用户手工配置IP地址也很不方便,这就需要一种新的方式来帮助设备获取IP地址,就是希望设备可以主动为自己指定一个可用的IP地址。
在IPV6环境下,IPV6协议本身就提供了设备自指定IP地址的能力,所以实现很简单,直接使用IPV6的协议支持就可以了。在IPV4环境下,Bonjour使用了随机指定IP地址的方法,首先为设备随机指定一个属于本地网段的IP地址,然后检查该地址在本地是否有冲突,如果有冲突就随机生成另一个新的IP地址,直到找到可用IP地址为止。
我在做测试的时候没有测试这部分,都是使用的DHCP的动态地址。以后有时间测试了这个部分后再和大家分享测试结果。

2.2 名称解析
在传统网络环境下,名称和IP地址的对应关系是通过DNS服务解析的。当一个设备需要访问一个域名,如“www.abc.com”,设备将“www.abc.com”发给DNS服务器,服务器返回该域名对应的IP地址,设备再使用返回的IP地址对目标服务器进行访问。
在没有中心服务器的网络环境中,没有DNS服务器提供域名解析服务,名称解析变成一个严重问题。针对这一问题,业界的解决方案是mDNS,中文叫“组播DNS”,在标准文档RFC6762中定义。
“组播DNS”的原理很简单,当一个设备需要解析一个名称时,如“abc.local.”,这个设备通过UDP协议向本地网络中的所有设备广播一个消息,问谁是“abc.local”,本地网络中如果有一个设备认为自己是“abc.local”,它就给出响应,说出自己的IP地址。因为“组播DNS”基于UDP协议,采用广播消息的方式,所以不需要一个中心服务器提供DNS解析服务就可以完成本地的名称解析。
Bonjour也是基于mDNS协议的,不过Bonjour在mDNS协议上作了扩展,加强了设备响应“组播DNS”请求的能力。在Bonjour协议下,应用只需要对某个名称进行注册,就可以将响应“组播DNS”请求的工作交由底层处理。也就是说在Bonjour协议下,应用不需要侦听本地网络的“组播DNS”请求并进行响应,这些工作由底层系统完成。为了区分全球域名和本地域名,mDNS协议使用“.local.”作为本地域名的根域名。

2.3 服务发现
当一个提供服务的设备获取IP地址,并自我指定一个域名后,其实还是不能满足用户的需求。因为用户需要的是某种服务,如打印服务,web服务,用户并不关心这些服务对应的服务器名称和它的IP地址。
为了让用户更容易发现本地网络中的各种服务,Bonjour为设备提供了服务发现的能力。
Bonjour提供的“服务发现”能力基于一个简单直接的规定,就是提供服务的设备在按以下标准对服务进行注册:“名称.服务类型.传输协议类型.local.”,比如如:“DamonWebServer._http._tcp.local.”,又比如“DummiesWebServer._http._tcp.local.”。
这样,当一个设备使用希望查找http服务的时候,Bonjour会去查找本地网络中注册过的包含”_http”的服务,然后将结果返回给用户选择。这时用户面对的是“DamonWebServer”和”DummiesWebServer”,用户可以不去关心到底这两个web服务到底在那台设备上,该设备的IP地址是什么。

如何使用Bonjour
对于最终用户来讲,Bonjour基本上是透明的,他们不需要了解如何去使用Bonjour,往往都是应用开发者去考虑如何使用Bonjour。
对于应用开发者来讲,他们需要考虑有两部分,一是如何作为Bonjour客户端去发现使用本地服务,二是如何作为服务端如何注册Bonjour服务

3.1 如何作为Bonjour客户端去发现本地服务
iOS开发可以使用NSNetService框架中的NSNetServiceBrowser类去发现本地服务。基本过程如下:首先创建NSNetServiceBrowser实例:

 serviceBrowser = [[NSNetServiceBrowser alloc] init];

然后指定NSNetServiceBrowser实例的代理,所指定的代理需要实现“NSNetServiceBrowserDelegate”协议。由实现以上协议的代理对服务查找相关的事件进行响应。这里指定本实例为NSNetServiceBrowser代理,由本例对服务查找的事件进行响应:

    serviceBrowser.delegate = self;

接着使用NSNetServiceBrowser实例的searchForServicesOfType方法查找服务,方法中可以指定需要查找的服务类型和查找的域。以下样例查找“local.”域中的“http”服务:

    [serviceBrowser searchForServicesOfType:@"_http._tcp." inDomain:@"local."];

最后,在“NSNetServiceBrowserDelegate”的以下方法中响应“didFindService”事件,就是找到服务的事件。其中的netService参数就是找到的服务,在netService参数中可以得到服务地址,服务主机名等信息。

 - (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser
 didFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing 
{}

3.2 如何作为服务端注册Bonjour服务
要注册成为Bonjour服务,开发者可以直接创建NSNetService实例,并通过initwithDomain: type: name: port:方法进行初始化,指定服务的域,类型,名称和端口,样例代码如下:

service = [[NSNetService alloc] initWithDomain:@"local."
type:@"_http._tcp." name:@"DamonWebServer" port:port];

NSNetService创建成功后,可以通过setDelegate指定代理,同时通过publish方法发布注册服务:

        [service setDelegate:self];
        [service publish];

其中指定的代理需要遵从“NSNetServiceDelegate”协议,可以对服务发布成功,发布失败等事件。
正常来讲,如果需要发布一个服务,需要在发布服务之前准备好服务并启动它。不过NSNetService的publish方法并不依赖它所发布的服务,不管服务是否准备好,是否启动,NSNetService的publish都可以成功将服务发布出去,只不过服务发布出去后其它使用这个服务的客户端会发现这个发布出来的服务是个无效服务。

在vue2中el-popover失效,刷新页面后正常显示,el-popover在子组件中,子组件中部分代码:<p v-if="!userName"> <el-popover placement="bottom-start" trigger="hover"> <div class="nav-game"> <ul> <li><el-button class="nav-avatar" type="primary" @click="gotoLogin()">登录</el-button></li> <li><el-link class="nav-avatar" type="primary" @click="gotoRegister()">用户注册</el-link></li> </ul> </div> <el-avatar slot="reference" size="medium" icon='el-icon-user-solid' src="https://i0.hdslb.com/bfs/face/9e684120bdf30f9c7130de8b3f3b004e589f46ef.jpg@96w_96h_1c.webp" ></el-avatar> </el-popover> </p> <p v-else> <el-popover placement="bottom-start" trigger="hover"> <div class="nav-game"> <ul> <li class="nav-a-text"> 用户名:{{ userInfo.name }} </li> <li class="nav-a-text"> id:{{ userInfo.u_id }} </li> <li> <el-menu :default-active="activeIndex" class="el-menu-demo nav-a-item" mode="horizontal" @select="handleSelect"> <el-menu-item index="1" style="border-bottom:none;"> <h3 class="nav-a-num">{{ userInfo.like_num }}</h3> <p class="nav-a-word">点赞</p> </el-menu-item> <el-menu-item index="2"> <h3 class="nav-a-num">{{ userInfo.fans_num }}</h3> <p class="nav-a-word">粉丝</p> </el-menu-item> <el-menu-item index="3"> <h3 class="nav-a-num">{{ userInfo.issue_num }}</h3> <p class="nav-a-word">作品</p> </el-menu-item> </el-menu> </li> <li> <el-menu class="el-menu-vertical-demo" :router="true" style="margin-top: 20px; border-right: none;"> <el-menu-item index="/center"> <i class="el-icon-user" style="color: blue"></i> <span slot="title" class="nav-a-affair">个人中心</span> </el-menu-item> <el-menu-item index="/center"> <i class="el-icon-tickets" style="color: blue"></i> <span slot="title" class="nav-a-affair">稿件中心</span> </el-menu-item> <div style="width: 100%;height: 2px;background-color: gray;"></div> <el-menu-item index="" @click="logout()"> <i class="el-icon-close" style="color: blue"></i> <span slot="title" class="nav-a-affair">退出登入</span> </el-menu-item> </el-menu> </li> </ul> </div> <div slot="reference"> <Avatar :asize="40"></Avatar> </div> </el-popover>
最新发布
03-30
<think>嗯,用户在使用Vue2的时候遇到了el-popover在子组件中失效,但刷新页面后又正常显示的问题。首先,我需要仔细看一下用户提供的代码片段,看看有没有明显的错误或者可能导致问题的原因。 首先,用户的条件渲染部分用了v-if和v-else,这里可能存在切换时el-popover没有正确销毁或重新初始化的情况。Vue的v-if指令在条件变化时会销毁和重新创建组件,可能导致内部状态丢失。比如,当userName从false变为true时,原来的el-popover会被销毁,新的会被创建,但可能在这个过程中某些事件监听没有正确绑定,导致hover触发不了。 然后,检查用户代码中的el-popover结构。注意到在v-else块里的el-popover,其slot="reference"对应的元素是自定义的Avatar组件。这时候需要确认Avatar组件是否正确渲染,并且能够触发hover事件。有时候,如果子组件内部的元素没有正确传递事件,可能会导致trigger="hover"失效。例如,如果Avatar组件内部没有正确处理鼠标事件,那么hover可能无法触发el-popover的显示。 另外,用户提到刷新页面后正常,这说明问题可能出现在动态切换的时候。可能是在组件初次渲染时,el-popover能正确绑定事件,但在条件切换后,新的el-popover没有正确初始化。这时候可能需要强制重新渲染组件,或者使用v-key来管理条件渲染的元素的重新创建。 还有一种可能是Element UI的版本问题。某些旧版本的el-popover可能存在bug,在动态渲染时表现不稳定。建议用户检查Element UI的版本,并尝试升级到最新版本,看是否能解决问题。 此外,代码中的el-menu组件嵌套在el-popover内部,可能存在层级或样式冲突,导致el-popover的位置计算错误。可以尝试调整样式,确保弹出层不受父容器溢出隐藏或其他布局影响。 最后,用户可能需要使用Vue的nextTick方法,在userName状态变化后等待DOM更新完成,再重新初始化el-popover的相关事件。或者,使用this.$forceUpdate()强制组件重新渲染,以修复状态不一致的问题。 总结下来,可能的解决方案包括:检查条件渲染导致的销毁和重新初始化问题,确保Avatar组件正确传递事件,升级Element UI版本,使用v-key强制重新渲染,调整样式避免布局问题,以及使用nextTick或forceUpdate来确保DOM更新后的正确行为。</think>在 Vue2 中遇到 `el-popover` 失效但刷新后正常的问题,通常由以下原因和解决方案构成: --- ### 一、问题核心原因分析 从代码片段看,`v-if` 条件渲染可能导致 **组件销毁/重建时未正确触发 Popover 的挂载机制**,具体表现为: 1. **`v-if` 切换导致 DOM 重新渲染**,但 Popover 的 `reference` 元素事件监听未正确绑定 2. **异步数据加载延迟** 导致 Popover 初始化时机异常 3. **ElementUI 版本兼容性问题** --- ### 二、分步解决方案 #### 方案 1:强制重新渲染组件(推荐) ```html <!-- 在 v-if/v-else 外层添加 key 强制销毁重建 --> <p v-if="!userName" key="unlogin"> <el-popover>...</el-popover> </p> <p v-else key="logined"> <el-popover>...</el-popover> </p> ``` #### 方案 2:手动控制 Popover 显示 ```javascript // 在数据更新后手动触发 this.$nextTick(() => { // 通过 ref 调用 Popover 方法 this.$refs.popover.doShow() }) ``` #### 方案 3:检查 Avatar 组件事件传递 ```html <!-- 确保自定义组件能传递鼠标事件 --> <div slot="reference" @mouseenter="handleMouseEnter"> <Avatar :asize="40"></Avatar> </div> ``` --- ### 三、关键代码优化建议 1. **升级 ElementUI 版本** ```bash npm install element-ui@latest --save ``` 2. **调整 Popover 挂载位置** ```html <el-popover placement="bottom-start" trigger="hover" popper-append-to-body <!-- 避免被父容器裁剪 --> > ``` 3. **添加状态监听** ```javascript watch: { userName(newVal) { this.$nextTick(() => { // 手动更新 Popover 实例 }) } } ``` --- ### 四、典型错误排查点 1. **`slot="reference"` 层级问题**:确保触发元素直接作为 `el-popover` 的子元素 2. **样式覆盖导致定位异常**:检查是否父元素有 `overflow:hidden` 3. **Vue 生命周期时序问题**:在 `mounted` 而非 `created` 阶段操作 DOM --- ### 五、完整修正示例 ```html <template> <div> <p v-if="!userName" key="guest"> <el-popover placement="bottom-start" trigger="hover" popper-append-to-body > <!-- 弹窗内容 --> <el-avatar slot="reference"></el-avatar> </el-popover> </p> <p v-else key="user"> <el-popover ref="userPopover" placement="bottom-start" trigger="hover" popper-append-to-body > <!-- 弹窗内容 --> <div slot="reference" @mouseenter="handleHover"> <Avatar :asize="40"/> </div> </el-popover> </p> </div> </template> <script> export default { methods: { handleHover() { this.$nextTick(() => { this.$refs.userPopover.doShow() }) } } } </script> ``` --- ### 六、延伸建议 - 使用 **Vue Devtools** 检查组件渲染状态 - 在 CSS 中添加调试样式:`border: 1px solid red` 观察元素布局 - 对于复杂场景建议改用 **`<el-tooltip>`** 或 **`<el-dropdown>`** 通过上述步骤可系统性解决 Popover 显示异常问题,建议优先尝试方案1+方案3的组合策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值