转自:http://www.cocoachina.com/bbs/read.php?tid=103810&page=e&#a
话说应该好些人想捉摸这个吧?我也有想,只是没空啊。看着不错,转一下
iOS 4.3出来的时候,苹果有了个神奇的功能airplay;它可以神奇的将iphone,ipad的音频传输到appletv, airport-express基座上;也可以将照片传输给apple tv通过HDMI投射到电视机上;
这个不亚于当年Mac os支持双屏拖拽般神奇.
那么这个技术是如何实现的呢?
历史上没被苹果收购前,有个airtunes的开源协议库,它可以实现随时随地的家庭音乐无线流媒体传输;
后来苹果收购了它,更名为airplay,将airtunes升级到airtune 2版本(但是大体基于原有版本扩充),增加了视频,照片的传输,完整的变为airplay非开源功能.
作为一个轻度影音发烧友,音效的追求是永无止境的,吃得消的支出才是合理的;而airplay作为家庭音乐的一个神器,必然想要收入囊中的.
那是不是必须要买apple TV呢?或者买airport express ?
两者价格都接近700,800;一个支持音视频;一个支持仅有音频,但是多出来一个双频路由器功能;
老实说,apple tv在美国很超值,但是无法越狱,谁会看和谐的youtube ?而且apple tv的越狱频率,远不如iphone ipad集万千宠爱于一身,砖家肯定不热衷越狱apple tv.
所以apple tv很不超值.但是,如何拥有airplay呢?
目前的现实解决方案,就是买支持airplay的音响设备;那我们目前可选的有Denon,Marantz,B&W,JBL,iHome;这些设备,我都听过;不得不说,真是蛋疼,一个个功能很挫,音质压根没达到起步水准.而面对家里的Bose135影院,我真是想瞬间给它插个airplay的翅膀.我想,很多家里已经有音响设备的,肯定希望就花一点点钱,就能实现airplay吧.
于是我就一头冲进去这airplay的世界.
好吧,科普一下,我们研究的是airplay的receiver,不是sender;所谓sender就是iphone 4s/ipad 2/3;
首先,ipad能在家庭无线网络里神奇般的搜寻到receiver;
搜到后,还能继续勾搭互联互通上;
基本推算出来的原理大概如此;
那么,搜到recevier并且确定receiver的IP;那么肯定是用了bonjour服务;其实就是mDNS这个技术;mDNS就是mini DNS的意思;公网用网址域名对应IP,那么小网络怎么办?只能寄托mDNS,即很小的微型DNS,这个功能是由路由器代替实现的;
原理如下,每当有局域网的client申请路由器代为广播,谁需要airplay服务就找他;那这个client就向路由器申请mDNS的service,提供名字,功能类型,端口,等等;
然后路由器会有一个hash表,存着各个client申请的service表;
每当有client希望找到airplay的服务的时候,它就会去问路由器,谁提供airplay服务?路由器会表里查询,并且告诉你IP;
同理,airprint也是这个类似原理;
话说前段时间做一个huawe的外包;丫的弄了个android机顶盒,要我实现airplay;结果仔细一问,就是mDNS发现机顶盒服务;然后用TCP控制机顶盒;更搞笑的是,当我正儿八经拿着mDNS的代码跟机顶盒联调的时候,居然说机顶盒没实现mDNS,真是无语国内外包的发自内心的偷懒省事,不专业.
看文档得知,Apple TV一共publish两个服务;一个是airtunes的ROAP协议;一个是airplay的service,包含照片,视频,镜像;
好吧,下面代码演练一下;
NSNetService*publish=[[NSNetService alloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:7000];
[publish publish];
以上在Mac OS下运行,可以在iphone, ipad的照片上看到airplay的选项;
可惜,当我尝试如法炮制roap的airtunes的时候,无法在ipad的ipod上看到神奇的airplay按钮了.
到底出错在哪儿?端口和服务字段不对?
噢,我有个办法,有个免费的airplay的server java版程序,能在ipod看到airplay;要不来个NSnetserviceBrowser,扫描一下人家神马端口不就结了;
折腾了半天,我发现每次端口都不一样;
于是我继续网络搜罗,功夫不有心人,有个AirView,有开源,是一个ipad版的airplay receiver;这个虽然有点儿绕远了,但是好歹的确能在ipod/镜像的地方看到airplay按钮.虽然功能不行,但是好歹,证明了这个代码能实现mDNS的正确引导;
于是顺藤摸瓜:
if(airplay== nil)
airplay= [[AirPlayControlleralloc] initWithWindow:window];
[airplaystartServer];
岂不是很明显的跟进去!
- (void)publishBonjour
{
if(type)
{
netService= [[NSNetServicealloc] initWithDomain:domaintype:typename:nameport:[asyncSocketlocalPort]];
[netServicesetDelegate:self];
}
其中domain= @"local.";
[httpServersetType:@"_airplay._tcp."];
这么推测下来,我的前期准备airplay都是对的,但是仅在photo里发现,肯定有问题;最终的问题,就是看端口了;
蛋疼的是,我看到这段代码
dispatch_sync(socketQueue, ^{
// No need for autorelease pool
if(socket4FD!= SOCKET_NULL)
result= [selflocalPortFromSocket4:socket4FD];
elseif(socket6FD!= SOCKET_NULL)
result= [selflocalPortFromSocket6:socket6FD];
});
又是G_C_D,又是block,去年CC DEV大会,还记得zenny兄讲G_C_D的好处,讲openCL,当时的感觉是,这些东西,都是务虚,不落地;
每次都是拿着个例子计算1累加到多少,我当时就想,老师连举例都很难编造个实在的案例,那我们学了有嘛用?
好吧,既然现在人家高手写个代码用这两货,我就补一下课,到底实在的靠谱用一下.
Block再补课
NSString* (^calculate)(NSString*,NSString*);
int(^Multiply)(int, int);
以上是两个Block的申明定义
可以放在头文件之上,即不要放在interface definition里
也可以放在implement里,注意,不要放在函数里,否则不具备函数块内可见
上面是两个申明的block类型的变量;可以理解为一个函数指针,比如calculate,Multiply两个函数指针;
calculate=^(NSString*part1,NSString*part2)
{
return[part1 stringByAppendingFormat:part2];
};
NSString*test=calculate(@"fuck",@"U");
//test is "fuckU"
Multiply= ^(intnum1, intnum2) {
returnnum1 * num2;
};
intresult = Multiply(7, 4); // result is 28
上面是两个函数指针,最终给予赋值;
calculate等于的东西,必须要跟类型匹配上;
下面的test即可执行了这个函数;结果也验证了;
好吧,这么理解:
如果^在括号里面,那么与^同在括号内的英文字符,代表这是一个”函数指针”的概念,类似快速引用; 例如: char (^square) (int); 前面是返回值类型char,后面是参数int类型;
如果^在括号外,那么就是一个具体的block的实现函数的抬头符.之后的内容,无非是参数,大括号,实现内容;可参考square = ^(int a ) {return a*a ;};
square(5)即是25;
总之,block基本就是这样;省去了你定义一个不必要的函数,然后再调用,烦死了;还要考虑备份现场;因为block是实时运算,运算的数据全部重新拷贝一份;你可以理解为new了一个程序在沙盒里计算,怎么着都不会影响;当然了,更复杂的有__block;唉.复杂的结果,就是代码可读性差;
注意,block是一个称呼;不是关键字; ^才是关键符号;
我个人觉得,在代码里用block单词作为block的”函数指针”的,都TMD脑子有病;这不故意混淆视听么?比如,你一个用来解析json的block,你丫干脆用JsonBlock,何必故意弄个这么绕口的?
那么block适合干啥?
我现在思维禁锢,因为之前没有^,我们也活得好好的,现在唯一想出来的好处,就是偷懒,随取随用,这要这个函数没有复用的必要,那就放心大胆的用吧.省了头文件定义,冗余的格式.
再补课GrandCentralDispatch
好吧,我看完51CTO的一篇文章,我彻底懂了.这货,就是为了解决NSThread解决不了的问题的;
是啊,多线程,就是多倍任务;经典的案例就是,UI不卡死,后台处理网络数据;
而用Queue来任务排队处理,经典的应用就是,请求有各种各样的类别,A类请求在A queue里排队; B类请求在B queue排队;这里A, B不一定是网络与本地的区分,更重要的是事务的区分,有人为的概念在其中;
dispatch_queue_t 真没啥可怕的,就当做ASIHttpRequestQueue一样,创建一个队列,然后你所有的操作,都是围绕这个队列里;
你可以添加任务,终止任务,执行任务;
连执行任务函数都非常类似
startSynchronous
dispatch_async
dispatch_sync
下面我抄袭一下
声明一个队列
如下会返回一个用户创建的队列:
dispatch_queue_t myQueue =dispatch_queue_create(“com.iphonedevblog.post”, NULL);
其中,第一个参数是标识队列的,第二个参数是用来定义队列的参数(目前不支持,因此传入NULL)。
执行一个队列
如下会异步执行传入的代码:
dispatch_async(myQueue, ^{ [selfdoSomething]; });
其中,首先传入之前创建的队列,然后提供由队列运行的代码块。
声明并执行一个队列
如果不需要保留要运行的队列的引用,可以通过如下代码实现之前的功能:
dispatch_async(dispatch_queue_create(“com.iphonedevblog.post”, NULL), ^{ [self doSomething]; });
暂停一个队列
如果需要暂停一个队列,可以调用如下代码。暂停一个队列会阻止和该队列相关的所有代码运行。
dispatch_suspend(myQueue);
恢复一个队列
如果暂停一个队列不要忘记恢复。暂停和恢复的操作和内存管理中的retain和release类似。调用dispatch_suspend会增加暂停计数,而dispatch_resume则会减少。队列只有在暂停计数变成零的情况下才开始运行。dispatch_resume(myQueue);
从队列中在主线程运行代码
有些操作无法在异步队列运行,因此必须在主线程(每个应用都有一个)上运行。UI绘图以及任何对NSNotificationCenter的调用必须在主线程长进行。在另一个队列中访问主线程并运行代码的示例如下:
dispatch_sync(dispatch_get_main_queue(), ^{[self dismissLoginWindow]; });
dispatch_get_global_queue
dispatch_get_main_queue
dispatch_get_current_queue
谁能告诉我以上3个queue的区别?
言归正传,回到这段代码
dispatch_sync(socketQueue, ^{
// No need for autorelease pool
if(socket4FD!= SOCKET_NULL)
result= [selflocalPortFromSocket4:socket4FD];
elseif(socket6FD!= SOCKET_NULL)
result= [selflocalPortFromSocket6:socket6FD];
});
原来,丫的就是在socketQueue的任务队列里,寻找端口号
实际在如下函数里,传入port为0,系统会自动给你分配一个随机端口;难怪丫的每次测试端口都不一样;我去!
success = [asyncSocketacceptOnInterface:interfaceport:porterror:&err];
网上的文章,说神马airplay,airtunes,aircontroll端口分别是7000,6000,6001的,你们对得起自己良心么?NND,端口就是无关因素.
好吧,既然代码一样,参数一致,端口又是无关因素,那么到底是什么导致它的app在系统各个airplay窗口都能识别到呢?
后来想了一个很简单的,我光看了startServer,还没看初始化server的构造函数呢.
于是看到了很重要的一段,原来NSNetservice的setTXTRecordData居然非常重要;
于是一番周折,终于我自己的testApp也能让ipod,photo,mirror的地方看到神奇的自己的选项了;
代码如下
NSNetService*publish=[[NSNetService alloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:56486];
NSDictionary*txtRecordDic=[NSDictionary dictionaryWithObjectsAndKeys:
@"0x7", @"features",
[DeviceInfoplatform], @"model",
[DeviceInfodeviceId], @"deviceid",
nil];
NSData*txtRecordData =nil;
if(txtRecordDic)
txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDic];
[publish setTXTRecordData:txtRecordData];//very Important
[publish publish];
尽管如此,我依然很不解,为什么要是ox7 ?为什么要有features?为什么有model?deviceID?
很简单,暴力测试一下,把所有的字符串改掉;
果然结果证明,只有deviceid是必备的,不能随意修改的;而且数值也必须要符合device ID的规则,例如: 10:9A:DD:65:19:3D
于是代码压缩成如下:
NSNetService*publish=[[NSNetServicealloc]initWithDomain:@"local."type:@"_airplay._tcp."name:@"Jacob"port:56486];
NSDictionary*txtRecordDic=[NSDictionarydictionaryWithObjectsAndKeys:
[DeviceInfodeviceId], @"deviceid",
nil];
NSData*txtRecordData =nil;
if(txtRecordDic)
txtRecordData = [NSNetServicedataFromTXTRecordDictionary:txtRecordDic];
[publish setTXTRecordData:txtRecordData];//very Important
[publish publish];
好吧,研究到底告一段落,这段研究,实现了mDNS最神奇的一部分,苹果的规则,要求airplay字段,tcp类型,local局域网内,并且附带一个text,内容必须是device id,而且数值要符合规范.
接下来的几天,我将继续研究airplay的实现原理;
比如iphone/ipad搜到airplay的receiver后,必然要有业务交集;比如send一个照片,send一段音频,放一段视频,等等,我作为receiver该如何交接请求等等.
分享到:
相关推荐
AirPlay 是苹果公司开发的一套协议族,用于实现各种媒体内容(如照片、音频、视频等)在 Apple TV 上从任何 iOS 设备或 iTunes 中播放。在此文档中,“iOS 设备”特指 iPhone、iPod touch 或 iPad。AirPlay 支持以下...
MDNS协议以太网报文,可以用wireshark软件打开,适用于学习mdns报文解析,学习各种工业以太网协议可参考本人其他下载文件
airplay-mdns-服务器 AirPlay mDNS广播服务器。 该模块本身不是AirPlay服务器,而是使用多播DNS广播其地址(运行此Node模块的计算机的mac / ip)。 安装 npm install airplay-mdns-server 用法 var mdns = require ...
arduino的MDNS库,开发测试时的wireshark抓包分析,已过滤其他杂包
MDNS JS,全称为Multicast DNS for JavaScript,是一种在JavaScript环境中实现的多播DNS(MDNS)技术。MDNS,也被称为Zeroconf或Bonjour,是一种网络服务发现协议,允许设备在本地网络上无需配置DNS服务器就能自动...
Apple的Bonjour服务是一种基于mDNS(Multicast DNS)技术的网络发现框架,它使得设备能够轻松地在...同时,熟悉Bonjour的底层实现也有助于理解苹果生态系统中其他基于mDNS的服务,如AirPlay的设备发现和连接过程。
AirPlay是苹果公司开发的一系列协议集合,用于在Apple TV上展示来自iOS设备或iTunes的各种类型的媒体内容。这里的“iOS设备”特指iPhone、iPod touch或iPad。AirPlay支持的主要场景包括: - **展示照片和幻灯片**:...
AirPlay是苹果公司开发的一套协议集合,用于实现各种媒体内容(如照片、音频、视频等)在iOS设备(如iPhone、iPod touch、iPad)、iTunes以及Apple TV之间的传输与展示。此文档将详细介绍这些协议的工作原理和技术...
简介Airplay2是apple在iOS11.3中新加的特性,用作视频和音频的局域网投放AirplayServer作为接收端,可以接收来自支持Airplay2设备的数据AirplayServer可运行在Android设备,代码99%是C语言编写,方便移植功能mDNS...
在Java中实现SMPP协议,可以让我们创建自己的短信网关,以便在不同系统之间集成短信功能。 该压缩包“Java实现的点对点短消息发送协议(smpp)开发包源码.7z”包含了用于Java平台的SMPP协议实现源代码。这个开发包...
mDNS的核心规范在RFC 6762中定义,它利用IP组播用户数据报协议(UDP)来发送数据包,特定的组播地址是224.0.0.251,端口号为5353。 mDNS的一个重要区别在于,所有的“mDNS”名称都以".local."结尾。当用户在浏览器...
本篇文章将深入探讨如何在QT环境中使用MDNS,并通过一个名为“QMdnsDemo”的示例项目来展示其快速设备发现的能力。 首先,理解MDNS的基本原理至关重要。MDNS基于DNS-SD(DNS Service Discovery)协议,利用UDP多播...
Android mDNS Apple mDNS堆栈是为... 为了在Android环境中使用,Apple的mDNS堆栈需要进行一些调整,并且需要某些包装层以确保可以与应用程序一起正确使用。 该项目的目的是提供一个基于Gradle的项目,使您可以使用简
在实际应用中,WiFi Display通常会结合MDNS来实现设备间的自动发现和连接。例如,当用户希望将手机上的视频无线传输到电视时,手机可以通过MDNS广播其WiFi Display服务,电视通过监听MDNS广播来发现可用的投屏源,...
Golang中的简单mDNS客户端/服务器库。 mDNS或多播DNS可以用于在本地网络上发现服务,而无需使用权威DNS服务器。 这启用对等发现。 重要的是要注意,许多网络都限制了多播的使用,这会阻止mDNS正常运行。 值得注意的...
标题中的"Python库 | anubi.mdns-0.0.4a0.tar.gz"表明这是一个与Python相关的库,名为anubi.mdns,版本号为0.0.4a0,其源代码被压缩在tar.gz格式的文件中。这种文件格式通常用于在Unix或Linux系统中分发软件包,它...
**mDNS在Go中的实现详解** mDNS,全称Multicast DNS,是一种基于DNS协议的本地网络服务发现技术。它允许设备在没有集中式DNS服务器的情况下进行服务的查询和发布,特别适合于局域网环境。Go语言作为现代、高效的...
### 广告宣传WSD打印机通过mDNS与DNS-SD记录的关键知识点 #### 一、概述 本文档主要介绍在Windows 10操作系统中如何利用DNS Service Discovery ...然而,在实现这些技术时,也需要关注其潜在的安全风险和兼容性问题。
在IT行业中,网络通信协议是实现设备间交互的关键技术,`.NET`框架为开发者提供了丰富的工具和库来处理这些任务。本话题将深入探讨`.NET`框架如何与`COAP`(Constrained Application Protocol)和`mDNS`(Multicast ...