游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

WebRTC学习笔记二基础概念

  一、WebRTC与架构
  简单来说,WebRTC是一个可以在Web应用程序中实现音频,视频和数据的实时通信的开源项目。在实时通信中,音视频的采集和处理是一个很复杂的过程。比如音视频流的编解码、降噪和回声消除等,但是在WebRTC中,这一切都交由浏览器的底层封装来完成。我们可以直接拿到优化后的媒体流,然后将其输出到本地屏幕和扬声器,或者转发给其对等端。
  WebAPI层:面向开发者提供标准API(javascirpt),前端应用通过这一层接入使用WebRTC能力。CAPI层:面向浏览器开发者,使浏览器制造商能够轻松地实现WebAPI方案。音频引擎(VoiceEngine):音频引擎是一系列音频多媒体处理的框架,包括从视频采集卡到网络传输端等整个解决方案。iSACiLBCOpus等编解码NetEQ语音信号处理回声消除和降噪视频引擎(VideoEngine):是一系列视频处理的整体框架,从摄像头采集视频、视频信息网络传输到视频显示整个完整过程的解决方案。VP8编解码jitterbuffer:动态抖动缓冲Imageenhancements:图像增益传输(Transport):传输会话层,会话协商NAT穿透组件RTP实时协议P2P传输STUNTRUNICE实现的网络穿越硬件模块:音视频的硬件捕获以及NetWorkIO相关
  虽然浏览器给我们解决了大部分音视频处理问题,但是从浏览器请求音频和视频时,我们还是需要特别注意流的大小和质量。因为即便硬件能够捕获高清质量流,CPU和带宽也不一定可以跟上,这也是我们在建立多个对等连接时,不得不考虑的问题。二、WebRTC的重要的类和API1。MediaStream(媒体流)和MediaStreamTrack(媒体轨道)
  这个类并不完全属于WebRTC的范畴,但是在本地媒体流获取,及远端流传到vedio标签播放都与WebRTC相关。MS由两部分构成:MediaStreamTrack和MediaStream。MediaStreamTrack媒体轨,代表一种单类型数据流,可以是音频轨或者视频轨。MediaStream是一个完整的音视频流。它可以包含0个MediaStreamTrack。它主要的作用就是确保几个媒体轨道是同步播放。2。Constraints媒体约束
  关于MediaStream,还有一个重要的概念叫做:Constraints(约束)。它是用来规范当前采集的数据是否符合需要,并可以通过参数来设置。基本constconstraint1{audio:true,是否捕获音频video:true是否捕获视频}详细constconstraint2{audio:{sampleSize:8,echoCancellation:true回声消除},video:{视频相关设置width:{min:381,当前视频的最小宽度max:640},height:{min:200,最小高度max:480},frameRate:{min:28,最小帧率max:10}}}获取指定宽高,这里需要注意:在改变视频流的宽高时,如果宽高比和采集到的不一样,会直接截掉某部分{audio:false,video:{width:1280,height:720}}设定理想值、最大值、最小值{audio:true,video:{width:{min:1024,ideal:1280,max:1920},height:{min:776,ideal:720,max:1080}}}
  对于移动设备来说,还可以指定获取前摄像头,或者后置摄像头:{audio:true,video:{facingMode:user}}前置{audio:true,video:{facingMode:{exact:environment}}}后置也可以指定设备id,通过navigator。mediaDevices。enumerateDevices()可以获取到支持的设备{video:{deviceId:myCameraDeviceId}}
  还有一个比较有意思的就是设置视频源为屏幕,但是目前只有火狐支持了这个属性。{audio:true,video:{mediaSource:screen}}
  【腾讯文档】FFmpegWebRTCRTMPRTSPHLSRTP播放器音视频流媒体高级开发资料领取
  FFmpegWebRTCRTMPRTSPHLSRTP鎾斁鍣闊宠棰戞祦濯掍綋楂樼骇寮鍙璧勬枡棰嗗彇3。获取设备本地音视频
  其中本地媒体流获取用到的是navigator。getUserMedia(),它提供了访问用户本地相机麦克风媒体流的手段。varvideodocument。querySelector(video);navigator。getUserMedia({audio:true,video:true},function(stream){拿到本地媒体流video。srcwindow。URL。creatObjectURL(stream);},function(error){console。log(error);});
  getUserMedia的第一个参数就是Constraint,第二个参数传入回调函数拿到视频流。当然你可以使用如下Promise的写法:navigator。mediaDevices。getUserMedia(constraints)。then(successCallback)。catch(errorCallback);4。RTCPeerConnection
  RTCPeerConnection,用于实现peer跟peer之间的NAT穿透,继而无需服务器就能传输音视频数据流的连接通道。
  这么说过于抽象,为了帮助理解,可以用一个不太恰当但有助于理解的比喻:RTCPeerConnection就是一个高级且功能强大的用于传输音视频数据而建立类似Websocket链接通道,只不过它可以用来建立浏览器
  之所以说是高级且强大,是因为它作为WebRTCweb层核心API,让你无须关注数据传输延迟抖动、音视频编解码,音画同步等问题。直接使用PeerConnection就能用上这些浏览器提供的底层封装好的能力。5。PeertopeerDataAPI
  RTCDataChannel可以建立浏览器之间的点对点通讯。常用的通讯方式有websocket,ajax和等方式。websocket虽然是双向通讯,但是无论是websocket还是ajax都是客户端和服务器之间的通讯,你必须配置服务器才可以进行通讯。
  而由于RTCDATAChannel借助RTCPeerConnection无需经过服务器,就可以提供点对点之间的通讯,无需(避免)服务器了这个中间件。varpcnewRTCPeerConnection();vardcpc。createDataChannel(mychannel);dc。onmessagefunction(event){console。log(received:event。data);};dc。onopenfunction(){console。log(datachannelopen);};dc。onclosefunction(){console。log(datachannelclose);};6。信令Signaling
  我们说WebRTC的RTCPeerConnection是可以做到浏览器间(无服务)的通信。
  但这里有个问题,当两个浏览器不通过服务器建立PeerConnection时,它们怎么知道彼此的存在呢?进一步讲,它们该怎么知道对方的网络连接位置(IP端口等)呢?支持何种编解码器?甚至于什么时候开始媒体流传输、又该什么时候结束呢?
  因此在建立WebRTC的RTCPeerConnection前,必须建立另一条通道来交这些协商信息,这些也被称为信令,这条通道成为信令通道(SignalingChannel)。
  两个客户端浏览器交换的信令具有以下功能:协商媒体功能和设置标识和验证会话参与者的身份(交换SDP对象中的信息:媒体类型、编解码器、带宽等元数据)控制媒体会话、指示进度、更改会话、终止会话等其中主要涉及SDP(offer、answer)会话描述协议,以及ICEcandidate的交换。
  这里需要注意的一点:WebRTC标准本身没有规定信令交换的通讯方式,信令服务根据自身的情况实现。一般会使用websocket通道来做信令通道,比如可以基于http:socket。io来搭建信令服务。当然业界也有很多开源且稳定成熟的信令服务方案可供选择。
  显而易见,在上述连接的过程中:呼叫端(在这里都是指代浏览器)需要给接收端发送一条名为offer的信息。接收端在接收到请求后,则返回一条answer信息给呼叫端。
  这便是上述任务之一,SDP格式的本地媒体元数据的交换。sdp信息一般长这样:v0o18379335896860187262INIP4127。0。0。1st00agroup:BUNDLEaudiovideoamsidsemantic:WMSyvKeJMUSZzvJlAJHn4unfj6q9DMqmb6CrCOTmaudio9UDPTLSRTPSAVPF11110310490810610513110112113126。。。。。。
  但是任务不仅仅是交换,还需要分别保存自己和对方的信息,所以我们再加点料:
  呼叫端创建offer信息后,先调用setLocalDescription存储本地offer描述,再将其发送给接收端。接收端收到offer后,先调用setRemoteDescription存储远端offer描述;然后又创建answer信息,同样需要调用setLocalDescription存储本地answer描述,再返回给接收端呼叫端拿到answer后,再次调用setRemoteDescription设置远端answer描述。
  到这里点对点连接还缺一步,也就是网络信息ICE候选交换。不过这一步和offer、answer信息的交换并没有先后顺序,流程也是一样的。即:在呼叫端和接收端的ICE候选信息准备完成后,进行交换,并互相保存对方的信息,这样就完成了一次连接。
  这张图是我认为比较完善的了,详细的描述了整个连接的过程。正好我们再来小结一下:基础设施:必要的信令服务和NAT穿越服务clientA和clientB分别创建RTCPeerConnection并为输出端添加本地媒体流。如果是视频通话类型,则意味着,两端都需要添加媒体流进行输出。本地ICE候选信息采集完成后,通过信令服务进行交换。呼叫端(好比A给B打视频电话,A为呼叫端)发起offer信息,接收端接收并返回一个answer信息,呼叫端保存,完成连接。三、WebRTC建立连接的关键ICE连接
  在交换SDP后,webrtc就开始真正的连接来传输音视频数据。这个建立连接的过程相当复杂,原因是webrtc既要保证高效的传输性,又要保证稳定的连通性。
  由于浏览器客户端之间所处的位置往往是相当复杂的,可能处于同一个内网段内,也可能处于两个不同的位置,所处的NAT网关也可能很复杂。因此需要一种机制找到一条传输质量最优的道路,而WebRTC正具备这种能力。
  首先简单了解以下三个概念。ICECanidate(ICE候选者):包含远端通信时使用的协议、IP地址和端口、候选者类型等信息。STUNTURN:STUN实现P2P型连接,TRUN实现中继型连接。两者实现均有标准协议。(参考下图)NAT穿越:NAT即网络地址转换,由于客户端并不能分配到公网IP,需要内网IP与公网IP端口做映射才能与外网通信。而NAT穿越就是位于层层Nat网关背后的客户端之间发现对方并建立连接。
  ICE连接大致的原理及步骤如下:发起收集ICECanidate任务。本机能收集host类型(内网IP端口)的candidate。通过STUN服务器收集srflx类型(NAT映射到外网的IP端口)的candiate。通过TURN服务器收集relay类型的(中继服务器的IP和端口)的candidate。开始尝试NAT穿越,按照host类型、srflx类型、relay类型的优先级去连接。
  以上,WebRTC便能找到一条传输质量最优的连接道路。当然实际情况并不是这么简单,整个过程包含着更复杂的底层细节。
  以下参考ICE协议下NAT穿越的实现(STUNTURN)1。首先来简单讲讲什么是NAT?
  原来这是因为IPV4引起的,我们上网很可能会处在一个NAT设备(无线路由器之类)之后。NAT设备会在IP封包通过设备时修改源目的IP地址。对于家用路由器来说,使用的是网络地址端口转换(NAPT),它不仅改IP,还修改TCP和UDP协议的端口号,这样就能让内网中的设备共用同一个外网IP。举个例子,NAPT维护一个类似下表的NAT表:
  NAT设备会根据NAT表对出去和进来的数据做修改,比如将192。168。0。3:8888发出去的封包改成120。132。92。21:9202,外部就认为他们是在和120。132。92。21:9202通信。同时NAT设备会将120。132。92。21:9202收到的封包的IP和端口改成192。168。0。3:8888,再发给内网的主机,这样内部和外部就能双向通信了,但如果其中192。168。0。3:8888120。132。92。21:9202这一映射因为某些原因被NAT设备淘汰了,那么外部设备就无法直接与192。168。0。3:8888通信了。我们的设备经常是处在NAT设备的后面,比如在大学里的校园网,查一下自己分配到的IP,其实是内网IP,表明我们在NAT设备后面,如果我们在寝室再接个路由器,那么我们发出的数据包会多经过一次NAT。2。NAT的副作用以及解决方案
  国内移动无线网络运营商在链路上一段时间内没有数据通讯后,会淘汰NAT表中的对应项,造成链路中断。这是NAT带来的第一个副作用:NAT超时。而国内的运营商一般NAT超时的时间为5分钟,所以通常我们TCP长连接的心跳设置的时间间隔为35分钟。
  而第二个副作用就是:我们这边文章要提到的NAT墙。NAT会有一个机制,所有外界对内网的请求,到达NAT的时候,都会被NAT所丢弃,这样如果我们处于一个NAT设备后面,我们将无法得到任何外界的数据。
  但是这种机制有一个解决方案:就是如果我们A主动往B发送一条信息,这样A就在自己的NAT上打了一个B的洞。这样A的这条消息到达B的NAT的时候,虽然被丢掉了,但是如果B这个时候在给A发信息,到达A的NAT的时候,就可以从A之前打的那个洞中,发送给到A手上了。
  简单来讲,就是如果A和B要进行通信,那么得事先A发一条信息给B,B发一条信息给A。这样提前在各自的NAT上打了对方的洞,这样下一次A和B之间就可以进行通信了。3。四种NAT类型:
  RFC3489中将NAT的实现分为四大类:FullConeNAT(完全锥形NAT)RestrictedConeNAT(限制锥形NAT,可以理解为IP限制,Port不限制)PortRestrictedConeNAT(端口限制锥形NAT,IPPort限制)SymmetricNAT(对称NAT)
  其中完全最上层的完全锥形NAT的穿透性最好,而最下层的对称形NAT的安全性最高。
  简单来讲讲这4种类型的NAT代表什么:如果一个NAT是FullConeNAT,那么无论什么IP地址访问,都不会被NAT墙掉(这种基本很少)。RestrictedConeNAT,仅仅是经过打洞的IP能穿越NAT,但是不限于Port。PortRestrictedConeNAT,仅仅是经过打洞的IP端口号能穿越NAT。SymmetricNAT这种也是仅仅是经过打洞的IP端口号能穿越NAT,但是它有一个最大的和Cone类型的NAT的区别,它对外的公网Port是不停的变化的:比如A是一个对称NAT,那么A给B发信息,经过NAT映射到一个Port:10000,A给C发信息,经过NAT映射到一个Port:10001,这样会导致一个问题,我们服务器根本无法协调进行NAT打洞。
  至于为什么无法协调打洞,下面我们会从STUN和TURN的工作原理来讲。4。STUNServer主要做了两件事接受客户端的请求,并且把客户端的公网IP、Port封装到ICECandidate中。通过一个复杂的机制,得到客户端的NAT类型。
  完成了这些STUNServer就会这些基本信息发送回客户端,然后根据NAT类型,来判断是否需要TURN服务器协调进行下一步工作。
  我们来讲讲这两步具体做了什么吧:第一件事就不用说了,其实就是得到客户端的请求,把源IP和Port拿到,添加到ICECandidate中。
  来讲讲第二件事,STUN是如何判断NAT的类型的:假设B是客户端,C是STUN服务器,C有两个IP分别为IP1和IP2(至于为什么要两个IP,接着往下看):
  STEP1。判断客户端是否在NAT后:B向C的IP1的pot1端口发送一个UDP包。C收到这个包后,会把它收到包的源IP和port写到UDP包中,然后把此包通过IP1和port1发还给B。这个IP和port也就是NAT的外网IP和port(如果你不理解,那么请你去看我的BLOG里面的NAT的原理和分类),也就是说你在STEP1中就得到了NAT的外网IP。
  熟悉NAT工作原理的朋友可以知道,C返回给B的这个UDP包B一定收到。如果在你的应用中,向一个STUN服务器发送数据包后,你没有收到STUN的任何回应包,那只有两种可能:1、STUN服务器不存在,或者你弄错了port。2、你的NAT拒绝一切UDP包从外部向内部通过。
  当B收到此UDP后,把此UDP中的IP和自己的IP做比较,如果是一样的,就说明自己是在公网,下步NAT将去探测防火墙类型,我不想多说。如果不一样,说明有NAT的存在,系统进行STEP2的操作。
  STEP2。判断是否处于FullConeNat下:B向C的IP1发送一个UDP包,请求C通过另外一个IP2和PORT(不同与SETP1的IP1)向B返回一个UDP数据包(现在知道为什么C要有两个IP了吧,虽然还不理解为什么,呵呵)。
  我们来分析一下,如果B收到了这个数据包,那说明什么?说明NAT来着不拒,不对数据包进行任何过滤,这也就是STUN标准中的fullconeNAT。遗憾的是,FullConeNat太少了,这也意味着你能收到这个数据包的可能性不大。如果没收到,那么系统进行STEP3的操作。
  STEP3。判断是否处于对称NAT下:B向C的IP2的port2发送一个数据包,C收到数据包后,把它收到包的源IP和port写到UDP包中,然后通过自己的IP2和port2把此包发还给B。
  和step1一样,B肯定能收到这个回应UDP包。此包中的port是我们最关心的数据,下面我们来分析:如果这个port和step1中的port一样,那么可以肯定这个NAT是个CONENAT,否则是对称NAT。道理很简单:根据对称NAT的规则,当目的地址的IP和port有任何一个改变,那么NAT都会重新分配一个port使用,而在step3中,和step1对应,我们改变了IP和port。因此,如果是对称NAT,那这两个port肯定是不同的。
  如果在你的应用中,到此步的时候PORT是不同的,那么这个它就是处在一个对称NAT下了。如果相同,那么只剩下了restrictcone和portrestrictcone。系统用step4探测是是那一种。
  STEP4。判断是处于RestrictConeNAT还是PortRestrictNAT之下:B向C的IP2的一个端口PD发送一个数据请求包,要求C用IP2和不同于PD的port返回一个数据包给B。
  我们来分析结果:如果B收到了,那也就意味着只要IP相同,即使port不同,NAT也允许UDP包通过。显然这是RestrictConeNAT。如果没收到,没别的好说,PortRestrictNAT。
  到这里STUNServer一共通过这4步,判断出客户端处于什么类型的NAT下,然后去做后续的处理:这4步都会返回给客户端它的公网IP、Port和NAT类型,除此之外:
  (a)如果A处于公网或者FullConeNat下,STUN不做其他的了,因为其他客户端可以直接和A进行通信。
  (b)如果A处于RestrictCone或者PortRestrictNAT下,STUN还会协调TURN进行NAT打洞。
  (c)如果A处于对称NAT下,那么点对点连接下,NAT是无法进行打洞的。所以为了通信,只能采取最后的手段了,就是转成CS架构了,STUN会协调TURN进行消息转发。
  5。TURNServer也主要做了两件事:为NAT打洞:如果A和B要互相通信,那么TURNServer,会命令A和B互相发一条信息,这样各自的NAT就留下了对方的洞,下次他们就可以之间进行通信了。为对称NAT提供消息转发:当A或者B其中一方是对称NAT时,那么给这一方发信息,就只能通过TURNServer来转发了。6。为什么对称NAT无法打洞:
  假如A、B进行通信,而B处于对称NAT之下,那么A与B通信,STUN拿到A,B的公网地址和端口号都为10000,然后去协调TURN打洞,那么TURN去命令A发信息给B,则A就在NAT打了个B的洞,但是这个B的洞是端口号为10000的洞,但是下次B如果给A发信息,因为B是对称NAT,它给每个新的IP发送信息时,都重新对应一个公网端口,所以给A发送请求可能是公网10001端口,但是A只有B的10000端口被打洞过,所以B的请求就被丢弃了。显然Server是无法协调客户端打洞的,因为协调客户端打得洞仅仅是上次对端为Server发送端口的洞,并不适用于另一个请求。
  最后的最后再补充一点,就是NAT打的洞也是具有时效性的,如果NAT超时了,那么还是需要重新打洞的。五、垫片兼容性
  一开始各个浏览器厂商,都会实现自己的一套API,诸如webkitRTCPeerConnection和mozRTCPeerConnection这样的差异,对于前端开发者当然是苦不堪言。而adapter。js正是为了消除这种差异,帮助我们可以按照规范来写我们的WebRTC代码。

椒盐杏鲍菇一种特殊的美味,好吃得停不下来!奇异的口感让人上瘾小贴士:杏鲍菇营养价值非常高,它富含蛋白质和人体必需的八种氨基酸成分,常食能有效提高人体免疫力。杏鲍菇味道鲜美,有鲍鱼脆嫩的口感以及杏仁清新的香味,因而得名杏鲍菇。每100克杏……长得英俊帅气,却败给一张大嘴,这6位男星的嘴巴太不懂事了都说明星的高颜值是进入娱乐圈的敲门砖,所以娱乐圈高颜值的男星也非常多。他们一般都符合大众的审美,帅得都很有特点。他们的三庭五眼都很出色,鼻子嘴巴也很优异,因为这都是是决定……国企改革看山东丨贫困村变网红村,从泰山九女峰看齐鲁样板如何打大众网海报新闻记者沈童泰安报道7月27日,国企改革看山东媒体采访团来到山东文旅集团首个签约并投入建设的乡村振兴项目泰山九女峰乡村振兴示范区进行参观调研。返乡创业、就……从大数据就可以知道,什么才是初高中生应该选择的热门专业初高考成绩已成定局,努力的环节流转到填报志愿。经历过高考的我们都懂,选择学校尚可参考等级和各种排名,判断专业好坏就大多靠兴趣和感觉了。就算是想无脑跟风走,这些年专业更迭的……四大创就业率超高的专业,中西餐都有随着经济的不断发展,很大一批人也步入了技能荒的时代。当今社会,没有一技傍身,可以说是寸步难行的。但学什么能既发展兴趣,又谋求生计呢?小新在这里为你推荐了几个烹饪专业,还在焦愁的……川菜典故爱吃鱼的你,一定不能错过这道菜泡豇豆煸鲫鱼泡豇豆煸鲫鱼是以鲫鱼、泡豇豆、蒜蓉、姜末等为原料制作而成。泡菜,指为了利于长时间存放而经过发酵的蔬菜。我国最早的诗集《诗经》中有中田有庐,中田有庐,疆埸有瓜。……天生好看的小米Civi,好看的不仅是外表哦距离小米的新机发布已经近在咫尺了,作为一个忠实的米粉来说,真的是很期待的。这次从小米预热的各种情况来看,小米Civi还是很与众不同的,并不是强大性能的宣传,反而是超高的颜值,只……不求人带货的人气和弹幕均为全平台第一,晚玉和鲨鱼也让人期待随着科技的发展和网络的普及,很多相关的产业也是都发展了起来,直播就是其中的一种,而一些主播也是凭借自己的技术,颜值等方面吸引了很多粉丝,不求人就是最典型的一个了,大家也知道,主……英雄联盟的召唤师技能前期14分钟只能传送防御塔随着版本改动,小龙buff的又一波加强,节奏明显加快,最近玩了几次,游戏时长反而比以往还要长久,有些逆版本了。还没有适应版本,版本又一次更改,召唤师技能的前14分钟改变,……能过滤空气和水中杂质双效净化功能的阿尔卡司净化无雾加湿器T7来自南方的朋友一定都对小时候那清新的空气,湿润的泥土香气记忆犹新。而来到城市尤其是像北京一样的北方大城市考学、工作后,才知道什么叫半天不擦一层灰,随处可见扬风沙!在这样干燥灰尘……兼具耐用与科技方便!华为智选VOC智能门锁S测评使用体验物联网IOT其实早已不是什么新鲜事物了,现如今智能家居的概念已经普及人心,尤其是年轻家庭往往都会选择不断推陈出新的新科技家居产品,比如智能照明、智能音箱、智能冰箱、洗衣机、电视……赫敏艾玛沃森颜值崩了?穿黑色羽毛裙太显老,其实礼服不背锅赫敏在我们的印象中一直都是麻瓜中的小仙女啊,颜值高、智商还爆表。但是,似乎欧美女星的花期都很短,艾玛沃森也不例外。她早前出席活动中身穿一袭黑白薄纱拼接的羽毛裙,并没有展现……
有什么东西会存放越久越好喝?并且会增值?茅台酒吧存放越久越好喝,这里的越久它是有期限的,在一定的时间范围内才成立,世界上没有一直、永远越放越好喝的东西。如果一定要问,首选的是酒了。但这酒,理论上是比……农村有哪一样美食能让你回味无穷的?我最喜欢吃的就是豆杂面红薯干面和高粮面三碰头做的芝麻锅饼,沾上辣椒酱吃着真香,在不然用这些杂面手擀面条下羊肉在放一些霜打的红薯叶,特别好吃一连吃了三大碗肚子撑得棒棒的还要再吃一……咸阳让你最难忘的美食是什么?必须是大唐购物中心,物美价廉的酸菜鱼,还有老街道的一家老字号云南过桥米线。原老西门口民族饭店的牛羊肉泡馍;原老街人民剧院西侧的大肉水饺;原手管局东侧的节约食堂……中国哪里才最有资格被称为小吃之都?看到这个问题,我也来回答一下。厨子从事餐饮业已经20多年,跑过不少地方。餐饮业一直流传首一句话:食在广州,味在成都。厨子刚好对这二个地方都非常熟悉,也非常认可这句话。虽然……大家觉得旅游城市里,青岛漂亮还是厦门漂亮?有幸都去过这个两个城市,青岛住过1个月,厦门住过1年,现在本人就定居于厦门,谈谈对这两座城市的对比,1厦门属于市中心海岛城市,岛外4四区都是海湾包裹着厦门本岛,而青岛属于海湾型……怎样浸泡玉米钓鲤鱼效果好?说到如何泡制玉米粒,我有两个方法。第一种:能长期保存的。我们常说的老坛玉米就是这种。首先将买回来的新鲜玉米粒全部剥下来,然后用清水淘洗一下,放入锅内,加水至没过玉米粒为止……外国投资者持续看好中国市场来源:人民网人民日报海外版位于江苏如皋经济技术开发区的威格(江苏)电气设备有限公司是一家主要生产低压以及中高压电机的外资企业。近年来,公司通过不断加大研发投入,进行自动化……东北菜为什么比南方分量大?进化论里有一个法则。环境决定功能。任何一个菜系形成都和当地的气候,人文物产,经济基础息息相关。东北菜立刻会想到最有特色的杀猪菜!东北菜的基础是各种大炖菜。为啥,因为……乳鸽汤用什么煲汤最好喝?乳鸽汤要怎么煲才鲜美无腥味,教你最正确的做法,简单易做,汤浓味美,营养还翻倍。用最诚挚的态度,加最专业的注解,给美食之旅带来不一样的风景。我是天然力,一个专注于美食的干饭……你遇到过最无耻的ampquot蹭饭ampquot,是什么样的我家兄妹三人,我居最末。由于哥哥出于种种原因,日子过得紧张:别的不说了,就连最基本的身上衣、口中食,也解决不了。因此到了他的第二个孩子出生之后,大侄女就成了常住联合国大使……影帝对决,飙出豆瓣9。6!豆瓣开分9。6,什么含金量?超万人评分,五星比例超过81。无数粉丝的高期待,它扛住了什么是追番仪式感?沐浴更衣后第一时间打开腾讯视频,点进更新。留……好友再次顶峰相见!苏翊鸣谷爱凌同日获奖牌,皆已有1金1银在手在2月15日下午进行的北京冬奥会单板滑雪男子大跳台决赛中,苏翊鸣凭借前两跳的惊艳发挥,以182。50的总分获得金牌。算上8天前获得的坡面障碍技巧银牌,3天后才年满18周岁的苏翊……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网