分类目录归档:cdn

fredzeng与你一起对cdn,cdn加速,cdn是什么,免费cdn,cdn公司,cdn厂商,cdn技术学习相关知识及探讨!

2020CES-美国拉斯维加斯电子展中国展商统计

2020年美国拉斯维加斯消费类电子产品展览会(CES2020)
举办时间:2020年1月07日~10日,展览四天
举办地点:拉斯维加斯会展中心
主办单位:美国消费者技术协会(简称CTA)

2020CES-美国拉斯维加斯电子展中国展商统计

2020CES-美国拉斯维加斯电子展中国展商统计展馆产品分类:
LVCC中央大厅(Central):以视听为主,大型企业做宣传的地方;
LVCC 北馆(North):苹果专区、汽车电子专区等;
LVCC南馆(South):
S1:数码音频类、家用连接器, 
S2:数字成像、影像产品、综合电子区、会议室、游戏专区、数码健康专区、3D打印、智能机器人、智能穿戴、综合电子区 
S3、S4:电脑周边,个人通讯类、无线通讯、家庭影院,互联网信息技术 ,综合电子区 
Venetian金沙馆(Sands)二楼:专业展区,按产品分配,有积分;

新南广场馆:国家馆、不分产品、按国家为区域按国家分区; 

2020CES-美国拉斯维加斯电子展中国展商统计

 2020CES-美国拉斯维加斯电子展中国展商统计

往届中国参展商的数据统计:
2010年第43届CES,有超过 2500 个企业参展,有 20000 多个新产品展出,其中 中国参展企业超过 300 家,包括了联想、华为、漫步者、海信、TCL、海尔、华硕、汉王等厂商。
这一年,CES 首次邀请中国企业家代表作主题演讲,演讲者是海信集团董事长周厚健。
2011年第44届CES,参展企业达到 2500 个,中国有超过 400 家企业参展,在 CES 参展规模上排名第三,最终达成价值 3153 万美元的贸易合同和 5799 万美元的意向性贸易协议。
2012年第45届CES,3100 家展商,仅参展的深圳企业就超过了 300 家。
2013年第46届CES,微软第一次缺席 CES,海信果断出击,仅用 45 分钟即与美国/电视服务提供商 Dish Network 盘下了原本属于微软的展位。
2014年第47届CES,1200 多家中国企业参展。
2015年第48届CES,全球展商3700家,中国企业1300多家。
2016年第49届CES,展览会上,共有来自150个国家的4300多家展商参加,中国企业1400多家,约20000多个新产品在展会上推出,展出净面积19万平方米,展览总面积为38万平方米,观众超过15.3万人次,其中国际观众达3.4万人次,5000家媒体报道此次盛会,共举办41场主旨演讲,200场学术研讨会,同期颁发了年度创新产品奖。其中Microsoft、IBM、Intel、MOTO、索尼、松下、三洋、夏普、先锋、东芝、飞利浦及中国的海尔、海信、康佳、联想等百余家国际大型知名企业参展。其中中国企业占参展总数的1/6,CES展正在成为彰显中国企业实力的国际舞台。
2017年第50届CES,中国企业占据重要地位,中国参展商2017将再次膨胀,达到1416家左右。
2018年第51届CES,中国参展商达到1500家左右,其中深圳企业482家
2019年第52届CES,中国参展商持续高涨,达到1500家以上,其中深圳企业临近500家

2020CES观展总结

CES2020在拉斯维加斯正式开幕,热衷科技创新的前哨用户,怎能错过全球创新风向标的“消费电子大展CES”!

美国时间1月7日,王煜全、吴声、万维钢三维老师终于“会师”完毕,3W组合正式合体,已开启在拉斯维加斯的前哨考察团观展之行。接下三天来将带领大家,洞察展会背后的产业趋势,挖掘全新产品背后的商业机会,梳理商业背后的逻辑。

下面为大家带来王煜全老师第一天的观展总结!

| 航空公司首次参展,“平行现实”值得重视

今年Delta航空公司首次参加CES,是第一次航空公司来参展。这件事本身体现了CES的一个姿态:从传统的只看重科技,扩展到了整个消费品领域,甚至现在连服务也越来越重视科技。

演讲里最吸引我的是Delta航空提到的“平行现实(Parallel Reality)。这个概念。可以说是原来“环境智能(Ambient Intelligence)升级版。我们希望跟环境能有一个背景层的智能互动的,就是说我有什么需求,环境是知道的。

比如我走到商店橱窗前,橱窗给我展示的商品就是我想买的。我看到广告牌,上面展示的广告就是我想看的,甚至我连续走过两个广告位,它们展示的广告是连续的,当然更不用说其他的互动了。

如果我每一次去到一个陌生环境,还需要我手动输入个人信息,环境才知道怎么跟我互动,这肯定是低效的。所以这件事其实由来已久,就是如何能够有更强的环境智能,但是一直解决不好。因为首先是需要有足够强的智能,然后需要有足够强的互动能力,现在这两件事慢慢都解决了。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

一方面人工智能不断发展,机器对人的理解甚至会超过人类自己。我们今天在金沙展馆的时候也看到了,有一个重要概念,叫预测性技术(Predictive Technology)。

人工智能的未来并不是给我们分析现状,而是告诉我基于现状会发生什么、未来会怎么样。面对消费者,更重要的不是知道他的特征,而是知道消费者将会需要什么商品,甚至在什么时候要什么商品,才能更好地给他服务。

所以人工智能使得环境对人的理解达到这地步了。另外一个就是互动技术的发展。现在随着5G到来,IoT迎来蓬勃发展。IoT的核心是物跟物的互联。

一直有个说法,当各种智能设备兴起,手机可能会消亡。我认为这是错的。为什么呢?因为手机是一个核心节点,是一个强大的随身的智能助理。它对你的理解超过所有其他设备,因为它天天随身跟着你。甚至你的智能可穿戴设备的数据,也都会同步到手机里进一步去处理和解读。所以手机是你的最随身最懂你的智能终端。

那就意味着手机将会和环境产生大量的互动。而所谓平行现实,也就是说现实能够跟你随时实现互动,你走到哪,这个懂你的环境就会跟到哪里。这个平行现实的实现,很有可能是通过物物互动来实现的。手机和环境产生智能连接,手机对你进行解读,产生预判,知道你想要什么,然后连接周围的环境,把相应的服务或者内容推给你。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

当然另一种可能性是,环境中的设备本身也能进行大量的信息采集了。我们看到另一个趋势就是,显示成了一个独立产业,以前我们叫电视机或者叫电脑显示器,现在显示器就是一个独立产业,显示屏越来越智能化。

现在出现一个变化就是,以后大多数显示屏上都会带上摄像头。因为和环境互动要完成一个闭环,要有数据的采集分析、解读和输出。传统的显示屏是一个标准的数据输出设备,现在显示屏实现了智能化,分析解读也有了。但是数据的采集是没有的,所以才会造成显示是千人一面的,所有人看都是一样的内容。

如果有摄像头,能够捕捉到人脸,知道是谁正在看,就可以给他推送他想看的东西,甚至可以实现从不同角度看到的图像是不一样的,这在技术上是可实现的。当然也可以和手机结合,实现内容精准推送。

这是一个更加完美的未来世界,你走到任何一个环境,它都是随身的,懂你的,能够和你实现个性化互动的。

| 以色列的创新,要辩证的思考学习

今天我们看了CES比较有特色的尤尼卡公园的展馆,尤其深入了解了以色列展区。额。以色列科技创新的特色是,一方面和中国一样勤奋,愿意在模式创新上动脑筋,比如说在没有电网的地方弄个临时的电动车充电站,这个市场比较狭小,因为全世界没有电网地方越来越少,那种地方基本上是人烟稀少的,可能就不适合电动车去那儿。

另一方面,值得我们思考的是,以色列的优势是知道怎么对接高校科技,首先以色列有好高校,还有美国大量的高校里面都有是犹太裔科学家,以色列还有一批人才很精通直接从科技做转化。

但是问题是,以色列公司的目标市场一般是锁定美国的,做完转化实现量产和推向市场,甚至将来上市,都是往美国走的。我们知道的很多知名的以色列企业,其实只是根在以色列,发展都在美国,比如著名的Mobileye。

大多数以色列企业还没有发展到对接量产的阶段,在很早期就会往美国转移。所以位于以色列的企业,一般都还在研发期的早期,在做原型设计。在研发期,它们就会往美国转移。到了美国以后,这些企业才会考虑市场、考虑量产。

中国现在有一个现实的问题,就是我们还没有办法对接太早的科技企业。中国的企业都比较偏后期,科技企业到了需要大规模量产的时候,中国企业能解决。但是还在产品原型设计改进的时候,中国企业就帮不上忙。

所以,以色列企业为什么不可以学,不是说人家的精神不可学,是说跟中国现有的制造型企业没有办法严丝合缝对接上,中间有个鸿沟。而这个沟是在美国得到弥补的,不是在以色列。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

这方面,中国的企业家确实需要进一步提升我们的产业对接能力,我们也在寻找真正有意愿和实力的“科技制造家“,希望能够跟他们一起对接全球科技创新,推动中国产业升级。

| 智能家居明显升级,会不会全面开花?

智能家居整体上比前几年更落地了,2C的产品越来越多,但其实中美的模式不太一样,美国是相对有中心系统的智能家居布局,它的中心是智能音箱。现在的胜出者其实已经有了,第一是亚马逊,第二是谷歌,苹果可能还有一系列的机会,其他的胜出者就不容易了,大格局基本上定了。

格局确定之后,大家也就容易做事了,因为智能家居产品基本和亚马逊和谷歌联通就覆盖掉90%的市场了,这也是为什么美国智能家居的大量的对接的产品多起来的原因,因为一旦标准化了,就敢放手去把我的系统接进去。

当然本身这些企业也在做各种努力,比如我们今天看了亚马逊的展馆,他的车载系统看似不是家居,但实际上后台是Alexa智能语音互动系统,表面是音箱,核心是智能语音互动。

另外,比如我前一阵刚买了一个新的微波炉,就是智能语音互动的。一旦语音互动的核心解决了,大家就会想我如何介入了,所以就开始滚雪球。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

中国始终没有形成这样一个前台的核心,前台核心要以硬件载体音箱为主,但是中国什么东西都是千团大战,音箱也是三四十家智能音箱,甚至更多智能音箱大战。

但实际上他们忽略了一个事实,智能音箱的核心不是智能音箱,而是用智能音箱作为硬件载体,后台是软件的语音互动支撑平台。中国语音互动支撑平台水平不够,只顾打音箱战了,所以到最后造成了一个结局就是,因为大家都有音箱,所以谁也不愿意介入对方的系统,反倒造成一些大的玩家没有办法整合,是分裂的。

从后台做整合是更科学的做法,就是不考虑前台的问题了,谁是第一接入点不重要,但是后台遵循同样的规则更重要。尤其是像涂鸦智能,我们为什么看好,因为它是一个相对第三方的开放平台。

如果阿里接入百度平台,百度接入腾讯平台,大家都是不放心的,但是不管百度、阿里还是腾讯接入涂鸦平台,这是可以的,因为你基本上就是一个后台。

所以中国的特点是前台不清晰,前台的争夺就还会延续很久,甚至说可能会出现无霸主的局面。但是后台能统一,这和国外是不太一样的。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

另外一个原因也很有意思,这在西方不太存在,因为西方都是独立的house,中国是大规模建商品房,现在大量的商品房造成了一个前装市场,就是说我实际上跟开发商谈,开发的时候,就把智能家居就引入了。

引入智能家居的时候,其实也要考虑这个问题,不愿意过分的品牌化,如果引入涂鸦智能这样的平台,我引入谁都是合理的,因为作为一个后台,我接入谁都可以接。但如果引入的是阿里的系统,首先得罪了百度,其次为什么要替阿里做宣传对吧?所以这就是涂鸦智能这样平台的机会,因为更偏向后台赋能,而且它的选择性更强,前台要用什么都可以。

所以中国的住房前装市场更集中居住的特点,也是涂鸦能够迅速起来的一个重要原因。我们今天在CES展台看到涂鸦的增长速度开始放快,原因很简单,就是他在前装积累了足够用户数的时候,他的to C市场,也就是它的后装市场就容易启动了。

但这个机会,地产自己做未必能走通。地产企业进入科技领域其实犯过很多错误,地产自己做智能家居平台肯定是错误之一。为什么?因为体量不够大,地产和阿里、百度有同样问题,地产不会用竞争对手地产公司的平台,我不会让你知道我的老底的。

那么就会带来一个问题,只能是地产自己的公司来做,但是中国的地产公司没有一家大到垄断到1/2,1/3的市场,虽然单个体量很大,但是整体来说比例很小,市场覆盖率不够。

另外一个很严重的问题是特别容易产生路径依赖,因为这是自己公司的产品,做成什么样都行,没有检验标准也没有第三方来评价如何算更好,这样的产品是没有办法做好的。

| CES创新奖应该更加创新

今天我们带大家去看了创新奖的集中展示区,总的来说并不那么令人惊艳。很多早就出现的概念,稍微做个改动,换个马甲就又推出来了,这肯定不是创新奖原来的目的,创新奖肯定是希望选最优的东西。

CES2020新机会:航空公司来了智能家居全面开花,以色列抱团创新

另一个问题是创新奖需要企业必须要来参加评选,咱们团里就有企业说他错过了,本来有很好的产品但是没来参评,那更不用说还有很多创新项目根本不来参加CES。所以如果只能在参加CES的小范围之内评奖,就会有偏差。

所以,我们计划在前哨大会上推出一个评选优秀科技消费品的奖项,会更公正一点,不管你来不来,只要产品够好,都给评上。我们的榜单就会在今年的前哨大会上揭晓,到时候请大家看看我们会评出怎样的创新产品。

CES第一天看展总结就到这里,明天我们继续。

伴随CES第一天结束,第一场晚间论坛也已成功举办,未来两天万维钢、吴声、王煜全三位老师的3W组合,将每晚持续给大家带来烧脑的思想盛宴,几位老师的精彩发言,我们将陆续整理,分享给大家,敬请期待。

智能家居控制入口研究

站在5年后的今天再审视这个议题,我们会发现情况在发生变化,据艾瑞咨询《2018年中国智能家居研究报告》[2] 中的调研结果,智能家居从业者眼中最看好的用户入口排名如下:

调研显示的结果极为分散,经过对“入口”概念的漫长争夺,智能家居从业者对入口仍无共识。

王学集表示,自己在创业初期也在寻找入口级设备的机会,直到半年后才逐渐意识到智能家居行业可能并没有一个确定的入口。他认为:

“智能家居是以AI的技术驱动了多设备联动,形成围绕式人机合一体验,目前看来很难有一个非常明确的设备能做到这样的水平。”

公共 NTP 服务器地址大全 Public NTP Server

NTP 是 Network Time Protocol 的简称,也就是网络时间协议。而 NTP 服务器是可以通过网络来同步时间的服务器。Windows 自带的 NTP 服务器都在美国,有时间经常无法访问。再加上有朋友跟我留言,想让我收集一下 NTP 服务器,所以建立了此页面。

本页优先提供国内可以使用的 NTP 服务器地址,然后也收集了一些国外知名的 NTP 服务器地址,提供给大家选择。

个人建议,国内用阿里云、国外用谷歌或者苹果。也有朋友经过测试苹果在国内也是可以使用的,大家可以试试看。

附:Windows 10 修改时间服务器的方法

如果有什么问题或建议可以跟我留言,戳⇒ 联系我

国内知名公共 NTP 服务器地址
Chinese Public NTP Server

[国内 NTP 服务器] · 国际 NTP 服务器

国家授时中心 NTP 服务器
NTSC NTP Server

ntp.ntsc.ac.cn

中国 NTP 快速授时服务
NTP ORG CN

cn.ntp.org.cn

教育网

edu.ntp.org.cn

国际 NTP 快速授时服务
Pool NTP ORG

cn.pool.ntp.org

阿里云公共 NTP 服务器
Aliyun NTP Server

time.pool.aliyun.com

time1.aliyun.com

time2.aliyun.com

time3.aliyun.com

time4.aliyun.com

time5.aliyun.com

time6.aliyun.com

time7.aliyun.com

腾讯云公共 NTP 服务器
Tencent Cloud NTP Server

time1.cloud.tencent.com

time2.cloud.tencent.com

time3.cloud.tencent.com

time4.cloud.tencent.com

time5.cloud.tencent.com

教育网(高校自建)
EDU NTP Server

ntp.sjtu.edu.cn

ntp.neu.edu.cn

ntp.bupt.edu.cn

ntp.shu.edu.cn

国际 NTP 服务器
Global Public NTP Server

[国际 NTP 服务器] · 国内 NTP 服务器

国际 NTP 快速授时服务
Pool NTP ORG

pool.ntp.org

0.pool.ntp.org

1.pool.ntp.org

2.pool.ntp.org

3.pool.ntp.org

asia.pool.ntp.org

谷歌公共 NTP 服务器
Google NTP Server

time1.google.com

time2.google.com

time3.google.com

time4.google.com

苹果公司公共 NTP 服务器
Apple NTP Server

time.apple.com

time1.apple.com

time2.apple.com

time3.apple.com

time4.apple.com

time5.apple.com

time6.apple.com

time7.apple.com

微软 Windows NTP 服务器
Microsoft Windows NTP Server

time.windows.com

美国标准技术研究院 NTP 服务器
NIST NTP Server

time.nist.gov

time-nw.nist.gov

time-a.nist.gov

time-b.nist.gov

香港天文台公共 NTP 服务器
Hong Kong Observator NTP Server

stdtime.gov.hk

浏览器开发模式:ServiceWorkers in Firefox and Chrome管理

Chrome和Firefox为用户提供了在浏览器中管理已注册Service Worker的选项,包括从浏览器中删除Service Worker的选项。

服务工作者是大多数现代浏览器支持的一项新兴功能,使站点和服务无需在浏览器中打开即可与浏览器进行交互。

将它们视为按需流程,可以使用推送通知和数据同步,或者使站点脱机工作。

当前没有将Web浏览器设计为在Service Worker在浏览器中注册时始终提示用户。当前,大多数情况下,这是作为后台进程发生的。

管理服务人员

显示通知

服务工作者可以自动注册,也可以在用户接受提示后注册。Pinterest是在Chrome或Firefox中访问该网站时会自动注册的网站。

用户不清楚这是因为它发生在后台。

Chrome和Firefox没有提供有关如何管理以前添加到浏览器的Service Worker的明确信息。尽管存在功能,但它们在此时或多或少对用户隐藏了,如果需要从浏览器中删除以前注册的工作人员,这将是一个问题。

本指南为您提供了在Firefox和Chrome中管理工作人员的方法。

有用的信息

  • 是注册服务工作者的页面。
  • 范围是指Service Worker控制的页面(接受来自其中的获取和消息事件)。
  • 脚本列出了Service Worker JavaScript文件的URL。

在Mozilla Firefox中管理服务人员

Firefox管理服务人员

Firefox用户可以通过以下方式在浏览器中管理所有注册的Service Worker:

  1. 在新选项卡或当前选项卡中加载about:serviceworkers,例如,通过复制和粘贴地址或将其添加书签并以此方式加载。
  2. Firefox在页面上显示所有注册的Service Worker。列出了每个Service Worker及其来源,范围,当前Worker URL,缓存名称和其他信息。
  3. 单击“取消注册”以从Firefox删除Service Worker,或单击“更新”以从其来源请求更新。

在Mozilla Firefox中禁用服务工作者

火狐禁用服务工作者

Firefox用户可以通过以下方式(通过我们广泛的Firefox隐私和安全设置指南列表)禁用浏览器中的Service Worker :

  1. 在浏览器的地址栏中加载about:config,然后按Enter。
  2. 确认如果显示通知,请小心。
  3. 使用搜索字段查找dom.service
  4. 找到dom.serviceWorkers.enabled,然后双击首选项名称以将其设置为false。这样做会禁用Mozilla Firefox中的Service Workers功能。

要撤消更改,请重复此过程,但请确保在完成操作后将首选项的值设置为true。

在Google Chrome中管理服务人员

镀铬服务人员
  1. 您需要在Chrome网络浏览器中加载chrome:// serviceworker-internals /网址,以打开已注册工作人员列表。
  2. Chrome显示的信息与Firefox略有不同,其中包括控制台日志,可能会派上用场。
  3. 点击取消注册按钮以从浏览器中删除所选项目,或开始激活它。

在Google Chrome中禁用服务人员

目前似乎没有办法在Chrome浏览器中禁用该功能。如果您找到了一种方法,请在下面发表评论,我将尽快更新文章。

Wi-Fi现场勘测,分析,故障排查

越 来越多的家庭和企业转向WiFi网络作为其首选的互联网接入的提供方式。无线网络对于终端用户更为便捷,使每个人都能够充分利用其移动设备的优势。无线网络相比传统的有线网络而言,也免除了线接的烦扰和限制。

从管理员的角度来说,WiFi网络虽然有很多的优势,但同时也伴随有一系列必须予以解决的其他挑战。这不仅仅只是为您的所有用户提供充足的连接的问题。您还需要了解您的网络的其他方面,如覆盖区域和信道重叠。

对于我们部分使用家庭WiFi网络的用户而言,我们就是自己的网络管理员。如果您是这样的情况或只是一位具有好奇心的终端用户,您需要了解的您的网络的其中一大特性就是WiFi信号强度。这个值对于您的网络活动效率是一个决定性的因素。让我们深度了解一下WiFi信号以及它们是如何影响您的无线网络使用的。

什么是WiFi信号?

WiFi网络使用无线电波来在不同的设备之间建立通信。这些设备可能包括计算机,移动手机,平板电脑或网络路由器。无线网络路由器是互联网或其他以太网络有线连接和无线连接设备之间的接口。

路由器对从WiFi网络用户那里接收来的无线电信号进行解码并将它们传至互联网。反过来,路由器会将从互联网接收的数据由二进位数据转换成无线电波,传送至使用同一网络的设备。

产生WiFi信号的无线电波使用的2.4 GHz和5 GHz的频带。这些频率要高于电视或手机所使用的频率,相对较低频率能够传递更多的数据。

WiFi信号在传输数据时使用的是802.11网络标准。WiFi网络采用了一系列不同协议的变体。您将会了解到的部分最常见的协议包括2.4GHz频带所使用的802.11n以及5GHz频带主要使用的802.11ac。其他协议您可能还会看到802.11b,它与802.11g是最慢标准。

良好的WiFi信号强度是怎样的?

整个网络覆盖区域内的WiFi信号强度直接影响着用户高效处理各种网络活动的能力。在深入探析怎样的信号强度才适合您进行相应的WiFi网络活动之前,让我们讨论一下 WiFi信号强度是如何测量的

信号强度是以dBm为单位的。它指的是分贝毫瓦,以从0到-100内的负值表示。因此,-40的信号相比-80的信号更强,因为-80距离0更远,因此是一个更小的数字。

dBm是一个对数性的,而不是线性的,这意味着信号强度之间的变化并不是平滑渐进式的。从这个层面来说,3 dBm的不同会使之前的信号强度减半或提升一倍。

能够影响到您的WiFi表现的背景噪音等级也是用dBm表示的。对于噪音等级而言,数值越接近0,就表示噪音等级越高。经测量的-10 dBm的噪音要高于-40 dBm的噪音。

下表揭示了您应当努力实现的最低信号强度,只有达到才能将您的WiFi网络用于各种不同目的。

信号强度限定符适合用途
-30 dBm极好这是可实现的最大信号强度,适用于任何一种使用情景。
-50 dBm极好此极好的信号强度适于各种网络使用情形。
-65 dBm非常好建议为智能手机和平板电脑提供支持。
-67 dBm非常好此信号强度对于网络电话和流媒体视频的使用是足够的。
-70 dBm可接受该等级是确保实现稳定的数据包传输的最低信号强度要求,对于网页冲浪和收发邮件的使用是足够的。
-80 dBm不良实现基本连接,但数据包传输不稳定。
-90 dBm非常差大多都是噪音,抑制大多数功能的实现。
-100 dBm最差全是噪音。

哪些因素会影响到WiFi信号强度?

能够影响到您的网络WiFi信号的有 一系列不同因素。其中部分因素有:

路由器位置

路由器位置的各个不同方面会影响到良好信号的传输表现。这些包括:

  • 路由器的高度 – 您应当将您的路由器尽可能放到高处。将它放在地板或较低的搁板上将影响到其提供强烈信号的能力。

  • 中心位置 – 如果您的路由器放在您的住宅或办公室的中心位置,那么您将获得 最佳WiFi信号覆盖。将路由器放在家里的角落将会导致WiFi信号泄漏并降低您的信号覆盖区域内的信号强度。

  • 来自其他设备的干扰 – 微波炉和无线电话可能使用的是与您的 WiFi路由器相同的频率,能够产生对信号等级造成影响的背景噪音。

  • 墙壁和地板 – 如果在连接设备的位置可以清楚地看到路由器的位置,那么您将会获得最佳WiFi信号。穿越 墙壁和地板进行传输的信号的强度会大打折扣。

保持您的路由器的更新

将您的路由器的设置改成自动固件升级以保持其处于高效运作状态。阶段性地重启您的路由器也是一个比较好的做法,有利于实现良好的信号覆盖。

距离路由器的距离

需要较强信号的设备,如那些用于玩游戏或观看视频的设备,将它们放在靠近路由器的位置可能会获得更好的信号表现。在一些情况下,您可能需要通过使用像路由器这样的附加设备来 扩展您的WiFi信号范围以增强WiFi信号强度。

mysql文本替换

UPDATE w_zshop_goods_sku_images SET path = REPLACE(path, ‘http://qq.com’,’https://qq.com’);
UPDATE w_zshop_commodities SET details = REPLACE(details, ‘http://qq.com’,’https://qq.com’);
UPDATE w_zshop_commodities SET params = REPLACE(params, ‘http://qq.com’,’https://qq.com’);
UPDATE w_users SET profile = REPLACE(profile, ‘http://qq.com’,’https://qq.com’);
UPDATE w_zshop_banners SET pic = REPLACE(pic, ‘http://qq.com’,’https://qq.com’);
UPDATE w_zshop_order_coms SET pic = REPLACE(pic, ‘http://qq.com’,’https://qq.com’);

coturn 添加redis实现对转发流量和转发时长的统计

前言

虽然已经用 coturn 来作为 webrtc 的转发服。 但是我们只做了一个最基本的静态key的加密校验而已。但是对于转发的流量和转发的时长,却没法统计。
但是事实上,对于转发的流量和时长对于我们的业务来说是非常必要的。
刚好 coturn 有提供了一个统计的redis配置,redis-statsdb,这个是就是 turnserver 会将一些统计的数据存到 redis 中。 然后我们去取就行了。
所以要在配置文件里面加上这一个配置:

1
redis-statsdb=”ip=59.57.xx.xx dbname=13 port=6379 connect_timeout=30″

具体文档:

-O, –redis-statsdb Redis status and statistics database connection string, if used (default – empty, no Redis stats DB used). This database keeps allocations status information, and it can be also used for publishing and delivering traffic and allocation event notifications. The connection string has the same parameters as redis-userdb connection string.https://github.com/coturn/coturn/wiki/turnserver
也就是这个参数是用来做状态统计的。 可以用来发布和传递流量, 还有断开和连接的webhook通知。通过这个统计的redis db,我们可以得到这一次转发的开始时间,结束时间,以及转发的流量。
大概的一个截图就是:

1

这边还有更详细的说明: schema.stats.redis
状态的key就是:

1
turn/user/<username>/allocation/<id>/status

举例:

1
turn/realm/pano/user/1531994598:fe909a0a4cde20be0a7bb9fbfdc8d6dc_21_24_18154723/allocation/003000000000002028/status
1

他的值有可能是 new lifetime=… 或者是 refreshed lifetime=…

一个用户名下可能有多个状态。
然后是订阅:

  • 通过订阅: turn/realm/*/user/*/allocation/*/status 这个主题,可以得到一些事件,比如 deleted 事件
  • 通过订阅: turn/realm/*/user/*/allocation/*/traffic 这个主题,可以得到一些信息,比如流量, 当这个配置被删除的时候, 也可以得到通知。

如果是订阅所有的配置,那么就是 turn/realm/*/user/*/allocation/*/total_traffic
因为不能过期,所以redis 的配置文件,要配置这两个:

1
2
timeout 0
tcp-keepalive 60

分析

假设我们已经在使用webrtc的应用上连接上turn server了,并且已经在转发了。那我们就可以看下这个redis跑的统计数据都是什么样的?
接下来先在redis 先sub一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
127.0.0.1:6379[13]> psubscribe turn/realm/*
Reading messages… (press Ctrl-C to quit)
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003326/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/003000000000003700/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/003000000000003700/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/003000000000003700/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/traffic”
4) “rcvp=1610, rcvb=1607159, sentp=438, sentb=22052”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/traffic”
4) “rcvp=1385, rcvb=1404252, sentp=663, sentb=31187”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/traffic”
4) “rcvp=1407, rcvb=1442093, sentp=641, sentb=30066”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/traffic”
4) “rcvp=1383, rcvb=1408780, sentp=665, sentb=31146”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003326/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003326/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003326/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003327/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417691:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/003000000000003705/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/total_traffic”
4) “rcvp=5785, rcvb=5862284, sentp=2407, sentb=114451”
1

可以看到进行一次连接的话,会分配好几条临时会话。 从下图来看的话,可以看到有 4 条,其实这四条都属于同一个连接。

接下来我们一条一条来分析:

  • 刚开始建立了一条 3326 的临时channel , 生命周期是 600s,也就是 10分钟
  • 然后又建立了一条 3324 的临时channel
  • 接下来的三条分别是,刷新一条 3700 的临时channel ,lifetime 为0,说明是要删除,所以接下来就收到一条 delete 的状态,说明这个临时topic要删除,然后当删除的时候,就会将这一段时间的流量(total_traffic )也传上来。
  • 接下来的4条,全部都是 3324 这一个topic的流量信息。包括收到的字节和发送的字节
  • 接下来的三条是3326,分别是将lifetime 为0,然后delete,最后关于这条临时channel 的流量过来。
  • 接下来的一条是 3324 这一条的lifetime 为 0, 然后是两条新的临时channel 的建立, 然后就是 3324 收到 delete 的状态,和总流量的通知。

总结一下:

1. 一次的webrtc 的turnserver 转发,其实会分配了好几个临时的 channel。

2. 每个 channel 的生命周期和对应的状态

每个 channel 刚开始创建的时候,生命周期只有 600s, 这个可以在 配置文件设置:

1
2
3
4
5
# Uncomment to set the lifetime for the channel.
# Default value is 600 secs (10 minutes).
# This value MUST not be changed for production purposes.
#
#channel-lifetime=600

而且刚开始创建就会有状态通知,内容就是 new lifetime=600
虽然每个channel的生命周期都是 600s, 但是并不意味着,600s 之后,这个channel就会被删除,其实不是的,这个是可以被续的,也就是如果这个 channel 被续了,那么就会有这个状态通知:

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532403222:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/004000000000003897/status”
4) “refreshed lifetime=600”

这个说明被续了一个周期了。当然也有不续的,这时候也会有这个状态,不过这时候 lifetime 是 0:

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532415366:07a9939e437c9bca97f67b4b74de6a2f_21_24_22819787/allocation/004000000000003905/status”
4) “refreshed lifetime=0”

一旦 lifetime 为0, 就说明这个channel要被删除了。 这时候就会收到 delete 状态:

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532415366:07a9939e437c9bca97f67b4b74de6a2f_21_24_22819787/allocation/000000000000001816/status”
4) “deleted”

这时候就说明这条channel已经被删除了。
而且每一个channel 的最长生命周期是可以配置的,默认是 3600s:

1
2
3
4
5
# Uncomment if you want to set the maximum allocation
# time before it has to be refreshed.
# Default is 3600s.
#
#max-allocate-lifetime=3600

所以一个完整的 channel 周期就是 创建-> 续周期(一次以上) -> 删除 (如果续的是 0)
针对这个,我后面试了一下, 在一次长达 一个多小时的webrtc连接, 那个转发流量的channel, 续周期续了7次, 一次 600s, 总的是 7 * 600 = 4200, 比3600 还大了,但是也还没有断开,所以这个也有点奇怪???

3. 流量信息

每一条channel 在生命周期之内,如果有进行流量转发的话,那么就会每隔一段时间就会有 traffic 这个状态过来,注意,如果是 stun 或者 local 这种不走转发的,那么是没有这个事件的:

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/traffic”
4) “rcvp=1383, rcvb=1408780, sentp=665, sentb=31146”

而且每次的channel 的生命周期结束之后,也就是 delete 状态,也会收到这一条生命周期的所有的转发流量数据。

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532417569:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003324/total_traffic”
4) “rcvp=5785, rcvb=5862284, sentp=2407, sentb=114451”

跟 traffic 不一样, traffic 是真的是转发有流量,才会每隔一段时间抛一次,而 total_traffic 是必会有的,不管是穿透还是转发, 只不过如果是穿透的话,那就是都是 0,比如:

1
2
3
4
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532415366:07a9939e437c9bca97f67b4b74de6a2f_21_24_22819787/allocation/004000000000003905/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”

每一个 delete 状态,都会跟着一个 total_traffic 事件。 而里面的 这些参数,其实都是 traffic 的所有对应参数的总和。
这四个参数分别是:

1
2
3
4
rcvp: 收到的数据包
rcvb: 收到的字节(byte)
sentp: 发送的数据包
sentb: 发送的字节(byte)

所以我们如果要统计这一次转发的时长的话,那么就要记录这次连接的所有的channel。也就是如果这一次的转发,有5个channel,那么就第一个channel创建的时候,就是开始时间, 当所有的channel 都断开的时候,那么就是结束时间。 一定要所有的channel 都断开,才算转发结束,
而且中间还会有不断的新的channel被创建,这些新的channel也要加入到map里面去。然后每一个channel断开,就从map里面去掉, 一直到最后map为空的话,才算转发结束。算转发的总流量也是一样,每一个channel都会有一段流量。 当所有的channel都断开的时候,加起来就是总的转发流量。

代码实现

刚开始是这样子写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package main

import (
“errors”
“fmt”
github.com/garyburd/redigo/redis
“iGong/util/log”
“strconv”
“strings”
“sync”
“time”
)

type OnlineTimeBucket struct {
Data map[string]*OnlineTimeBucketSub
Locker *sync.Mutex
}

func NewOnlineTimeBucket() *OnlineTimeBucket {
return &OnlineTimeBucket{
Data: make(map[string]*OnlineTimeBucketSub),
Locker: &sync.Mutex{},
}
}

type OnlineTimeBucketSub struct {
Timer map[string]*OnlineTime
}

type OnlineTime struct {
Start int64
End int64
Delete bool
TotalTraffIc bool
Rcvp int
Rcvb int
Sentp int
Sentb int
}

var onlineTimeBucket *OnlineTimeBucket
var serverTime = time.Now().Unix()

/*
turn/realm/pano/user/1531882310:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/000000000000000495/status new lifetime=600
turn/realm/pano/user/1531881513:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/001000000000000462/status refreshed lifetime=0
turn/realm/pano/user/1531882310:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/004000000000000899/status deleted
turn/realm/pano/user/1531881513:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/001000000000000462/total_traffic rcvp=18612, rcvb=894124, sentp=104268, sentb=114206669
turn/realm/pano/user/1531881926:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/004000000000000896/traffic rcvp=1847, rcvb=2052114, sentp=201, sentb=10652
*/

func TurnServerStats() {
conn, err := RedisTurn.GetConn()
sub := redis.PubSubConn{Conn: conn}
if err != nil {
log.Error(err)
}
defer conn.Close()
onlineTimeBucket = NewOnlineTimeBucket()
sub.PSubscribe(“turn/realm/*”)

for {
switch n := sub.Receive().(type) {
//case redis.Message:
// fmt.Printf(“Message: %s %s\n”, n.Channel, n.Data)
case redis.PMessage:
err = forwardStats(n)
if err != nil {
continue
}

case error:
fmt.Printf(“error: %v\n”, n)
return
}
}

}

func forwardStats(data redis.PMessage) (err error) {
fmt.Printf(“PMessage: %s %s %s\n”, data.Pattern, data.Channel, data.Data)
event, deviceId, channel, err := decodeChannel(data.Channel)
if err != nil {
log.Error(err)
return err
}
log.Info(event, deviceId)
switch event {
case “status”:
//统计时长 和在线状态
onlineStatus := decodeDataWithStatus(data.Data)
log.Info(onlineStatus)
addOnlineTime(deviceId, channel, onlineStatus)
case “total_traffic”:
//统计流量 这个事件过来说明转发已经结束 并且只有一个会话是有值的
trafficMap, err := decodeDataWithTraffic(data.Data)
if err != nil {
return err
}
//rcvp 接收到的包数量 rcvb 接收到的流量 sentp 发送的包数量 sentb 发送的包流量
log.Info(trafficMap)
addFlow(deviceId, channel, trafficMap)

}
return

}

func decodeChannel(channel string) (event string, deviceId, channelId string, err error) {
args := strings.Split(channel, “/”)
if len(args) != 8 {
err = errors.New(“channel fail .”)
return
}
event = args[7]
deviceId = strings.Split(args[4], “:”)[1]
channelId = args[6]
return

}

func decodeDataWithStatus(data []byte) (onlineStatus int) {
args := strings.Split(string(data), “=”)
switch args[0] {
//新建
case “new lifetime”:
onlineStatus = 0
//刷新
case “refreshed lifetime”:
onlineStatus = 1
//移除
case “deleted”:
onlineStatus = 2
default:
onlineStatus = 1
}
return
}
func decodeDataWithTraffic(data []byte) (stats map[string]int, err error) {
args := strings.Split(string(data), “,”)
if len(args) != 4 {
err = errors.New(“traffic data fail”)
return
}
stats = make(map[string]int)
for _, v := range args {
statsInfo := strings.Split(v, “=”)
s, _ := strconv.Atoi(statsInfo[1])
stats[strings.TrimLeft(statsInfo[0], ” “)] = s
}
return
}

//记录转发时长
func addOnlineTime(deviceId, channelId string, onlineStatus int) {
if onlineStatus == 1 {
return
}
onlineTimeBucket.Locker.Lock()
timer := time.Now().Unix()
if _, ok := onlineTimeBucket.Data[deviceId]; !ok {
j := new(OnlineTimeBucketSub)
j.Timer = make(map[string]*OnlineTime)
onlineTimeBucket.Data[deviceId] = j
}
switch onlineStatus {
case 0:
log.Info(“new life_time”)
var onlineModel = new(OnlineTime)
onlineModel.Start = timer
onlineTimeBucket.Data[deviceId].Timer[channelId] = onlineModel
case 2:
log.Info(“delete life_time”)
if _, ok := onlineTimeBucket.Data[deviceId].Timer[channelId]; !ok {
var onlineModel = new(OnlineTime)
onlineModel.Start = serverTime
onlineTimeBucket.Data[deviceId].Timer[channelId] = onlineModel
}
onlineTimeBucket.Data[deviceId].Timer[channelId].End = timer
onlineTimeBucket.Data[deviceId].Timer[channelId].Delete = true
}
onlineTimeBucket.Locker.Unlock()

}

//记录转发流量并入库
func addFlow(deviceId, channelId string, trafficMap map[string]int) {
onlineTimeBucket.Locker.Lock()
if _, ok := onlineTimeBucket.Data[deviceId]; !ok {
j := new(OnlineTimeBucketSub)
j.Timer = make(map[string]*OnlineTime)
onlineTimeBucket.Data[deviceId] = j
}

onlineTimeBucket.Data[deviceId].Timer[channelId].Rcvb = trafficMap[“rcvb”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Rcvp = trafficMap[“rcvp”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Sentb = trafficMap[“sentb”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Sentp = trafficMap[“sentp”]
onlineTimeBucket.Data[deviceId].Timer[channelId].TotalTraffIc = true
var isDelete = true
for _, v := range onlineTimeBucket.Data[deviceId].Timer {
if v.Delete == false || v.TotalTraffIc == false {
isDelete = false
}
}

if isDelete {
//计算时间,,流量
var rcvb, rcvp, sentb, sentp int
var end int64
start := time.Now().Unix()
for _, v := range onlineTimeBucket.Data[deviceId].Timer {
rcvb += v.Rcvb
rcvp += v.Rcvp
sentp += v.Sentp
sentb += v.Sentb
if start > v.Start {
start = v.Start
}
if end < v.End {
end = v.End
}
}
accountInfo := strings.Split(deviceId, “_”)
if len(accountInfo) < 4 {
log.Info(“deviceId len fail “, deviceId)
}
total_time := onlineTimeBucket.Data[deviceId].Timer[channelId].End – onlineTimeBucket.Data[deviceId].Timer[channelId].Start
err := InsertTurnServerLogs(accountInfo[3], accountInfo[0], accountInfo[1], accountInfo[2], start, end, total_time, rcvp, rcvb, sentp, sentb)
if err != nil {
log.Error(err)
}
onlineTimeBucket.Data[deviceId].Timer = nil
delete(onlineTimeBucket.Data, deviceId)
}

onlineTimeBucket.Locker.Unlock()
}

按照上面的算法来做的话,当我在用web测试的时候,会出现一个情况:

就是如果 coturn 的连接断开了。但是对于这个username, 他还是会保持一两条临时 channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/000000000000001857/traffic”
4) “rcvp=1466, rcvb=1325047, sentp=582, sentb=29696”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003357/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003357/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003357/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/000000000000001857/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/003000000000003734/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/001000000000003358/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/000000000000001857/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532433896:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/000000000000001857/total_traffic”
4) “rcvp=1466, rcvb=1325047, sentp=582, sentb=29696”
1

明明我连接断开了,但是又重新连接了两条。而且这两条根本不会释放

这个就会导致 isDelete 的条件没法成立,因为总有新的链接没有释放。
而且后面还发现,一次转发,虽然有好几条channel存在,但是真正转发流量的channel只有一条,而且过了一个多小时,还是没有断。所以原则上我们只要去判断这一条channel,就可以知道本次转发的时长和流量了。 其他的channel都不要管。但是如果是stun这种穿透的,因为没有转发流量,所以根本不知道那一条的channel,才是起作用的那一条????

后面发现这种情况其实是一个bug, 之所以浏览器刷新还保持两条turn channel 是因为浏览器在刷新的时候,其实没有明确的给手机端发送断开webrtc的信号。导致手机端其实还不知道webrtc断开了。所以手机端其实是对turnserver连接进行重连了, 这也是为啥会有新的两条channel 的问题。
我试了其他端,比如 ios 端,如果是断开的话,是不会有遗留turnserver 的channel 的,最后全部都会被清掉。因此对于web端,在刷新浏览器,或者是退出页面的时候,都要给手机的发送断开webrtc的信号,并且断开本地的webrtc连接。
所以web端的代码得改下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 关掉 webrtc
stopWebRtcServer: function () {
var self = this;
// 给手机端发送一个webrtc 关闭的指令
self.mqttSocket.pub(
self.subCommonStr + ‘toTarget’,
{
“method”: “webrtc.stop”,
},
{
useAes: true,
}
).done(function (data) {
console.log(“%c accept stop:” + JSON.stringify(data), “color:red”);
});
// 清空状态统计的定时
clearTimeout(self.rtcStateMap.timeId);
// 关闭signal长连接
self.mqttSocket.close();
// 关闭webrtc 连接
self.rtcSocket.close();
},

其中 rtcSocket 的 close 事件为:

1
2
3
4
5
6
7
close: function(){
// 停止 video 传输
var video = this._pc.getRemoteStreams()[0].getVideoTracks()[0];
video.stop();
// 关掉 pc 对象
this._pc.close();
}
1

先停止video传输,再释放掉 pc 对象。这时候本来 webrtc work 的时候,是这样的:

看到的消息就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/001000000000004071/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/004000000000004390/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/000000000000002210/status”
4) “new lifetime=600”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/004000000000004390/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/004000000000004390/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/004000000000004390/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/001000000000004071/traffic”
4) “rcvp=1546, rcvb=1442311, sentp=502, sentb=24802”
1
1

就是建了3条 channel ,然后删掉一条 390 ,保留两条, 后面分别是 210 和 071

这时候我点击 停止webrtc 按钮,图片传输就会停止了。这时候webrtc 就断开了。

这时候就收到了断开的消息了, 210 和 071 都断开了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/000000000000002210/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/001000000000004071/status”
4) “refreshed lifetime=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/000000000000002210/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/000000000000002210/total_traffic”
4) “rcvp=0, rcvb=0, sentp=0, sentb=0”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/001000000000004071/status”
4) “deleted”
1) “pmessage”
2) “turn/realm/*”
3) “turn/realm/pano/user/1532677507:ac5fa606abf5ae9d02bd4206625b4911_21_24_18152992/allocation/001000000000004071/total_traffic”
4) “rcvp=4595, rcvb=4367095, sentp=1549, sentb=74756”
1

这时候channel 就全部为空了。

所以要这样才可以。 但是问题来了,刚才是我们手动点击 停止 webrtc 按钮的,当然流程是对的。
但是如果是直接刷新浏览器呢,那可是来不及处理的????
事实上也是有这个问题,如果直接刷新浏览器,并且不作处理的时候,就会出现最早之前的那种情况,就是手机端认为还连着,所以 turnserver 又建了两条新的 channel 。
所以我后面加了这个事件 onbeforeunload

1
2
3
4
5
6
// 这时候要设置一个unload事件,不然如果直接浏览器刷新的话,手机端是不知道webrtc不用了,他还会连接 turnserver, 导致turnserver 的session 一直在,后面没法算时间
// 设置unonload, 防止直接刷新浏览器的时候,没有传 webrtc.stop 指令
window.onbeforeunload = function(){
self.stopWebRtcServer();
return true;
};
1

每次用户刷新的时候,这时候就会弹出这个窗口,当用户点击 重新加载的时候, 这时候 stop 操作就已经处理完了。

因此就成功关闭了,缺点就是用户每次刷新都会弹窗,并且要点击重新加载。

所以服务端来说的话,整个连接过程中创建的channel 都要做判断,只有当channel 全部断掉的时候, 才判断这个连接结束,这时候才要算时间。虽然每一次连接都有一条channel 在发送流量。所以如果 traffic 有值的话,那么就是这一条有流量统计。所以如果有收到有流量的 total_traffic 的事件 ,那么应该就是 relay 的连接断开了。这时候就可以计算时长并入库了。
所以修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
package main

import (
“errors”
“fmt”
github.com/garyburd/redigo/redis
“iGong/util/log”
“strconv”
“strings”
“sync”
“time”
)

type OnlineTimeBucket struct {
Data map[string]*OnlineTimeBucketSub
Locker *sync.RWMutex
}

func NewOnlineTimeBucket() *OnlineTimeBucket {
return &OnlineTimeBucket{
Data: make(map[string]*OnlineTimeBucketSub),
Locker: &sync.RWMutex{},
}
}

type OnlineTimeBucketSub struct {
Timer map[string]*OnlineTime
}

//map 存在线时间,结束时间-开始时间,,结束之后再存入表

type OnlineTime struct {
Start int64
RefreshedFlow int64
End int64
Delete bool
TotalTraffIc bool
Rcvp int
Rcvb int
Sentp int
Sentb int
}

var onlineTimeBucket *OnlineTimeBucket
var serverTime = time.Now().Unix()

/*
turn/realm/pano/user/1531882310:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/000000000000000495/status new lifetime=600
turn/realm/pano/user/1531881513:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/001000000000000462/status refreshed lifetime=0
turn/realm/pano/user/1531882310:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/004000000000000899/status deleted
turn/realm/pano/user/1531881513:7ed1019263ce5e09f3d40a1ca15ae992_21_24_18152992/allocation/001000000000000462/total_traffic rcvp=18612, rcvb=894124, sentp=104268, sentb=114206669
turn/realm/pano/user/1531881926:26e4cd38fa6fc39fa8fc40fb25fe558c_21_24_18152649/allocation/004000000000000896/traffic rcvp=1847, rcvb=2052114, sentp=201, sentb=10652
*/

func TurnServerStats() {
conn, err := RedisTurn.GetConn()
sub := redis.PubSubConn{Conn: conn}
if err != nil {
log.Error(err)
}
defer conn.Close()
onlineTimeBucket = NewOnlineTimeBucket()
sub.PSubscribe(“turn/realm/*”)

for {
switch n := sub.Receive().(type) {
//case redis.Message:
// fmt.Printf(“Message: %s %s\n”, n.Channel, n.Data)
case redis.PMessage:
err = forwardStats(n)
if err != nil {
continue
}
case error:
fmt.Printf(“error: %v\n”, n)
return
}
}
}

func forwardStats(data redis.PMessage) (err error) {
fmt.Printf(“PMessage: %s %s %s\n”, data.Pattern, data.Channel, data.Data)
event, deviceId, channel, err := decodeChannel(data.Channel)
if err != nil {
log.Error(err)
return err
}
log.Info(event, deviceId)
switch event {
case “status”:
//统计时长 和在线状态
onlineStatus := decodeDataWithStatus(data.Data)
log.Info(onlineStatus)
addOnlineTime(deviceId, channel, onlineStatus)
case “traffic”, “total_traffic”:
//统计流量 这个事件过来说明转发已经结束 并且只有一个会话是有值的
trafficMap, err := decodeDataWithTraffic(data.Data)
if err != nil {
return err
}
//rcvp 接收到的包数量 rcvb 接收到的流量 sentp 发送的包数量 sentb 发送的包流量
log.Info(trafficMap)
addFlow(deviceId, channel, trafficMap, event)

}
return
}

func decodeChannel(channel string) (event string, deviceId, channelId string, err error) {
args := strings.Split(channel, “/”)
if len(args) != 8 {
err = errors.New(“channel fail .”)
return
}
event = args[7]
deviceId = strings.Split(args[4], “:”)[1]
channelId = args[6]
return
}

func decodeDataWithStatus(data []byte) (onlineStatus int) {
args := strings.Split(string(data), “=”)
switch args[0] {
//新建
case “new lifetime”:
onlineStatus = 0
//刷新
case “refreshed lifetime”:
onlineStatus = 1
//移除
case “deleted”:
onlineStatus = 2
default:
onlineStatus = 1
}
return
}
func decodeDataWithTraffic(data []byte) (stats map[string]int, err error) {
args := strings.Split(string(data), “,”)
if len(args) != 4 {
err = errors.New(“traffic data fail”)
return
}
stats = make(map[string]int)
for _, v := range args {
statsInfo := strings.Split(v, “=”)
s, _ := strconv.Atoi(statsInfo[1])
stats[strings.TrimLeft(statsInfo[0], ” “)] = s
}
return
}

//记录转发时长
func addOnlineTime(deviceId, channelId string, onlineStatus int) {
if onlineStatus == 1 {
return
}
timer := time.Now().Unix()
if _, ok := onlineTimeBucket.Data[deviceId]; !ok {
j := new(OnlineTimeBucketSub)
j.Timer = make(map[string]*OnlineTime)
onlineTimeBucket.Locker.Lock()
onlineTimeBucket.Data[deviceId] = j
onlineTimeBucket.Locker.Unlock()
}
onlineTimeBucket.Locker.RLock()
switch onlineStatus {
case 0:
log.Info(“new life_time”)
var onlineModel = new(OnlineTime)
onlineModel.Start = timer
onlineTimeBucket.Data[deviceId].Timer[channelId] = onlineModel
case 2:
log.Info(“delete life_time”)
if _, ok := onlineTimeBucket.Data[deviceId].Timer[channelId]; !ok {
var onlineModel = new(OnlineTime)
onlineModel.Start = serverTime
onlineTimeBucket.Data[deviceId].Timer[channelId] = onlineModel
}
onlineTimeBucket.Data[deviceId].Timer[channelId].End = timer
onlineTimeBucket.Data[deviceId].Timer[channelId].Delete = true
}
onlineTimeBucket.Locker.RUnlock()

}

//记录转发流量并入库
func addFlow(deviceId, channelId string, trafficMap map[string]int, event string) {
if _, ok := onlineTimeBucket.Data[deviceId]; !ok {
j := new(OnlineTimeBucketSub)
j.Timer = make(map[string]*OnlineTime)
j.Timer[channelId].Start = time.Now().Unix()
onlineTimeBucket.Locker.Lock()
onlineTimeBucket.Data[deviceId] = j
onlineTimeBucket.Locker.Unlock()

} else {

}
onlineTimeBucket.Locker.RLock()
if event == “total_traffic” {
onlineTimeBucket.Data[deviceId].Timer[channelId].Rcvb = trafficMap[“rcvb”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Rcvp = trafficMap[“rcvp”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Sentb = trafficMap[“sentb”]
onlineTimeBucket.Data[deviceId].Timer[channelId].Sentp = trafficMap[“sentp”]
onlineTimeBucket.Data[deviceId].Timer[channelId].TotalTraffIc = true
var isDelete = true
if trafficMap[“rcvb”] == 0 {
//当穿透的时候流量为0,并且不会保存回话,所以需要判断所有会话是否关闭 当转发的时候,流量会大于0 ,但是可能会保留回话,所以转发的时候只需要判断流量过来就可以结束
for _, v := range onlineTimeBucket.Data[deviceId].Timer {
if v.Delete == false || v.TotalTraffIc == false {
isDelete = false
}
}
}
if isDelete {
onlineTimeBucket.Locker.RUnlock()
insertFlowLogs(deviceId)
return
}
} else {
onlineTimeBucket.Data[deviceId].Timer[channelId].RefreshedFlow = time.Now().Unix()
}

onlineTimeBucket.Locker.RUnlock()
}

func insertFlowLogs(deviceId string) {
log.Info(“start insertFlowLogs=>”,deviceId)
var rcvb, rcvp, sentb, sentp int
var end int64
start := time.Now().Unix()
for _, v := range onlineTimeBucket.Data[deviceId].Timer {
rcvb += v.Rcvb
rcvp += v.Rcvp
sentp += v.Sentp
sentb += v.Sentb
if start > v.Start {
start = v.Start
}
if end < v.End {
end = v.End
}
}
accountInfo := strings.Split(deviceId, “_”)
if len(accountInfo) < 4 {
log.Info(“deviceId len fail “, deviceId)
}

err := InsertTurnServerLogs(accountInfo[3], accountInfo[0], accountInfo[1], accountInfo[2], start, end, end-start, rcvp, rcvb, sentp, sentb)
if err != nil {
log.Error(err)
}
onlineTimeBucket.Locker.Lock()
onlineTimeBucket.Data[deviceId].Timer = nil
delete(onlineTimeBucket.Data, deviceId)
onlineTimeBucket.Locker.Unlock()
}

//程序关闭时调用,,记录 使用时长
func forceInsertTurnServerUseLogs() {
onlineTimeBucket.Locker.Lock()
log.Info(“start forceInsertTurnServerUseLogs”)
for k, v := range onlineTimeBucket.Data {
//取其中一个的开始时间就可以
var start = time.Now().Unix()
var end = time.Now().Unix()
var rcvb, rcvp, sentb, sentp int
for _, v := range v.Timer {
rcvb += v.Rcvb
rcvp += v.Rcvp
sentp += v.Sentp
sentb += v.Sentb
if start > v.Start {
start = v.Start
}
}
useTime := end – start
accountInfo := strings.Split(k, “_”)
if len(accountInfo) < 4 {
log.Info(“deviceId len fail “, k)
}
err := InsertTurnServerLogs(accountInfo[3], accountInfo[0], accountInfo[1], accountInfo[2], start, end, useTime, rcvp, rcvb, sentp, sentb)
if err != nil {
log.Error(err)
}
}
onlineTimeBucket.Locker.Unlock()
}

如果是穿透的话,因为没有流量,所以每次收到 total_traffic的时候,都要判断是不是所有的channel 都断开了。如果是的话,那么就是连接断开了。这时候就统计时长并且入统计。

ssh登录很慢解决方法

使用ssh客户端(如:putty)连接Linux服务器,可能会等待10-30秒才有提示输入密码。严重影响工作效率。登录很慢,登录上去后速度正常,这种情况主要有两种可能的原因:

1. DNS反向解析问题

OpenSSH在用户登录的时候会验证IP,它根据用户的IP使用反向DNS找到主机名,再使用DNS找到IP地址,最后匹配一下登录的IP是否合法。如果客户机的IP没有域名,或者DNS服务器很慢或不通,那么登录就会很花时间。

解决办法:在目标服务器上修改sshd服务器端配置,并重启sshd

  1. vi /etc/ssh/sshd_config
  2. UseDNS no

2. 关闭ssh的gssapi认证

用ssh -v user@server 可以看到登录时有如下信息:

  1. debug1: Next authentication method: gssapi-with-mic
  2. debug1: Unspecified GSS failure. Minor code may provide more information

注:ssh -vvv user@server 可以看到更细的debug信息

解决办法:

修改sshd服务器端配置

  1. vi /etc/ssh/ssh_config
  2. GSSAPIAuthentication no

可以使用ssh -o GSSAPIAuthentication=no user@server登录

GSSAPI ( Generic Security Services Application Programming Interface) 是一套类似Kerberos 5的通用网络安全系统接口。该接口是对各种不同的客户端服务器安全机制的封装,以消除安全接口的不同,降低编程难度。但该接口在目标机器无域名解析时会有问题

使用strace查看后发现,ssh在验证完key之后,进行authentication gssapi-with-mic,此时先去连接DNS服务器,在这之后会进行其他操作

OpenSSH_6.6.1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 56: Applying options for *
debug1: Connecting to kr.worthcloud.cn [107.150.126.57] port 36000.
debug1: Connection established.
debug1: identity file /home/fredzeng/.ssh/id_rsa type -1
debug1: identity file /home/fredzeng/.ssh/id_rsa-cert type -1
debug1: identity file /home/fredzeng/.ssh/id_dsa type -1
debug1: identity file /home/fredzeng/.ssh/id_dsa-cert type -1
debug1: identity file /home/fredzeng/.ssh/id_ecdsa type -1
debug1: identity file /home/fredzeng/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/fredzeng/.ssh/id_ed25519 type -1
debug1: identity file /home/fredzeng/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.6.1
debug1: Remote protocol version 2.0, remote software version OpenSSH_7.4
debug1: match: OpenSSH_7.4 pat OpenSSH* compat 0x04000000
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: server->client aes128-ctr hmac-sha1-etm@openssh.com none
debug1: kex: client->server aes128-ctr hmac-sha1-etm@openssh.com none
debug1: kex: curve25519-sha256@libssh.org need=20 dh_need=20
debug1: kex: curve25519-sha256@libssh.org need=20 dh_need=20
debug1: sending SSH2_MSG_KEX_ECDH_INIT
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ECDSA 66:cc:4d:4a:e0:88:3d:77:5f:a4:fd:5f:ff:05:5c:94
debug1: Host ‘[kr.worthcloud.cn]:36000’ is known and matches the ECDSA host key.
debug1: Found key in /home/fredzeng/.ssh/known_hosts:3
debug1: ssh_ecdsa_verify: signature correct
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: Roaming not allowed by server
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: publickey
debug1: Trying private key: /home/fredzeng/.ssh/id_rsa
debug1: Trying private key: /home/fredzeng/.ssh/id_dsa
debug1: Trying private key: /home/fredzeng/.ssh/id_ecdsa
debug1: Trying private key: /home/fredzeng/.ssh/id_ed25519
debug1: Next authentication method: password

不看好5G的14个理由(附精彩辩论)

6月18日,一位网名为“蓝色海岸”的业内人士,先后在c114论坛发文《不懂国内为什么那么看好5G?》,阐述了他不看好5G的8个理由,引发众多移动通信业界人士回帖反驳。6月22日,“蓝色海岸”再发文《不懂国内为什么那么看好5G之二—绝地反击》,阐述了他不看好5G的另外6个理由,再次引发众多移动通信业界人士回帖反驳。

这场辩论很精彩,很多地方都说得有理有据,有些观点/说法也具有一定的思辨性,下文就摘录其中的精彩内容,供大家参考。

网名为“蓝色海岸”的业内人士:

1、5G网速达10G, 不可能,即使能达到,也毫无意义,因为没有需要,现在光宽带的网速,EPON是1000兆,GPON是2500兆,可实际开通的一般是20兆,有很少部分用户开通到了100兆,为什么不开通2500兆,因为没必要,没意义,现在高清视频的带宽一般在2至3兆,就算下载需要更快一点,但也有限,因为人眼和人脑是有带宽的,太高了人的眼睛和大脑接受不了,就没意义了。既然光宽带都不需要那么高的网速,手机会需要吗?手机的屏幕就那么小,4K清晰度有意义吗?超过人眼能够识别的极限,标清和4K不是一样吗?

2、千兆无线路由器早已有之,但是市场反映平淡,很显然,只增加最后一公里的带宽,骨干网带宽不增加,也就没有意义了,这就好比只加宽家门口的马路,不加宽主干街道、国道和省道的公路,速度能提高吗?运营商和设备商就是这样愚弄用户的,说是增加到百兆带宽了,可是实际上只是增加了一点点。

3、服务器和手机的处理能力有限,宽带网一端连接的是服务器,也就是电脑吧,一端连接的是手机,电脑的PCIE总线速度已达6G以上,但是硬盘速度也就几百兆,而且这样一台服务器要连接很多用户,就算把电脑的处理能力全部用于一个用户,极限也就几百兆,而手机的处理能力就更弱了,这时候电脑的处理能力变成瓶颈了,网络两端的速度上不去,只是一味的增加中间部分,会有效果吗?

4、现在5G的宣传都说下载一部高清视频只要几秒云云,实际上只要下载视频的速度大于播放的速度,人就感觉不到网速的快慢,手机是一边放视频,一边下载的,你让手机下载那么快干什么呢?让手机几秒钟下载完,就可以歇着了,你心疼它累吗?

5、九几年的时候,上网很卡,后来带宽增加了,上网不卡了,看视频还是很卡,用的很不爽,运营商就告诉用户,是带宽不足,你要把56K猫换成ADSL,有了ADSL,又说要换光宽带,有了光宽带,又说要换4G,这似乎已经形成习惯和趋势了,实际上,上网和看视频,ADSL足够了。为什么ADSL不行,那是骨干网带宽不足,为什么骨干网带宽不足,是因为运营商之间互掐,以及骨干带宽资费不肯下调。接入网的带宽早已远超需要,GPON,EPON,千兆以太网,千兆无线路由器,10G以太网,这些技术早就成熟了,可是下载高清视频的速度也没有说就几秒啊,5G同样是在接入网段的技术,凭什么这么一个接入网段的技术,就能大幅度的提升网速呢?现在有个口号,叫4G改变生活,5G改变社会,的确,3G的带宽只有2兆多一点,而且是很多人分着用,是少了一点儿,但是4G看来是足够了,在没有Iphone的时候,3G搞了10年也发展不起来,那是因为没有需要,同样的5G想要发展起来,需求在哪里呢?

6、现在有了高清视频,有了1080P,甚至有了4K电视,网络快一点,还是有那么一点点道理,离开了大屏电视,就手机那么一个小小的屏幕,它的显示面积就那么一点点,它要那么大带宽干什么呢?再说了,有多少人在室外一边走路一边抱个手机看电影?恐怕没有吧,一般都是看看微信呀,上个网什么的,就这么点需要,难道上百兆的4G还满足不了?恐怕几兆就足够了吧。

7、我们的网速到底有多快,我可以告诉,平均每个用户就100K多一点,但是这是平均值,实际上由于统计复用的原因,并不是所有人都在不停的占用带宽,而是轮流使用的,所以大家使用的实际带宽还是挺大的,几兆应该是有的,如果用户和服务器同处网络边缘,也就是通过CDN加速,再宽一些也是可以达到的。这就好比马路就那么宽,但并不是所有人都站在马路上一样。你要更宽,那就变成经济问题了, 并不是技术实现的问题,试想如果带宽像空气和阳光一样随处可得,尽管没人离得开空气,可是又有谁能把空气变成钱呢?这种事情运营商会干吗?这就是说有价值并不等于有价格,没有价格也就是没有经济趋动力,没有经济趋动力谁肯干呢?骨干网平均带宽100多K,接入网要上一万兆,这意义又在哪里呢?

8、 这些都撤远了,就说通信行业,九十年代炒的火热的ISDN,那时候互联网还不普及,ISDN可以通两部电话,还可以通可视电话,可是可视电话因为视频压缩算法不过关,带宽低图像质量差,画面小,无法激起消费者的购买欲望,很多公司投入几亿几十亿美金,都丢到水里了。阚凯力阚大炮当时看准了ISDN没有未来,ISDN技术可以在一根电话线上装两部电话,可是他认为只要再接一条电话线就可以办到了,那样显然更便宜,何必为这个要搞一代新技术,更何况有多少家庭会需要两部电话啊?接下来是ATM,ATM把主要吸引点放在视频业务上,结果同样因为视频技术不过关,ATM无用武之地,胎死腹中。

楼主并不是说5G永远没价值,至少是在目前能看得见的未来,价值还体现不出来,这就很危险了,你说现在看不到也想不到的应用,就炒得火热,到处宣传到处试点,搞了几个试点是花了好几亿,这是在糊弄老百姓啊,还是在套取国家资金啊?阚大炮当年认为3G是皇帝的新装,结果3G的投资的确打了水漂,有人辩解说3G积累的技术,在5G时代用得上了,还要领先国际水平,可是阚大炮的眼光是很毒的,被他识破的东西要想翻身如同登天,3G时代积累的那些技术能否用在5G上,看来很大的可能性还得落空。

9、 5G现在的确吹的有点邪乎了,刚开始说要搞1G带宽,后来4G+出来了,能上300多兆,这样看来1G带宽好像也不是什么太有吸引力的概念了,然后就搞出个10G出来,这简直就是放卫星了。固网和光宽带现在实用带宽也就几十兆,现在10G的光宽带也已经实用化了,据说到2019 100G的PON标准也将发布,可是用户实际开通的带宽到底有多少呢?这种脱离实际应用,单纯的技术引导能走得远吗? 论坛里有人说5G是低延迟,这恐怕说的是基站到用户之间100多米的延迟吧,这100多米产生的延迟在整个通信链路中微不足道,基站到城域网,再到骨干网,用的就是光纤了,请问这段延迟是怎么解决的?这早就超过5G技术的范围了。决定延迟的根本原因,还是在于城域网和骨干网,这都不是5G技术能解决的问题了,你说5G能解决,那就明确说明这部分是怎么解决,你不要说那些通信专家肯定考虑到了,他们到底是怎么考虑的?

5G号称低延迟,连郎咸平都出来说话了,说5G的速度快于人的神经的反应速度,在胳膊上扎一针,大脑还没感觉,5G网另一端就已经知道了,这种说法的确够让人震惊,可是光通信的速度早就有这么快了,可就是没人那么说,这不是故弄玄虚糊弄无知群众么?5G厂商够厉害,连著名经济学家都搬出来造势了。5G要想实现低于光宽带的延迟,那得费九牛二虎之力,这一点连任正飞自己都承认还解决不了,可是媒体上都吹的跟真的一样了。

10、5G所使用的频段是毫米波,在这个波段,电波基本是直线传播,绕射和透射能力都很差了,另外5G基站的覆盖范围也很小,可能只有几百米吧,发射功率和带宽呈正相关,带宽加大,发射功率急剧增加,而手机的发射功率是有限的,这就决定了5G基站的覆盖范围很小,大概100多米到几百米的样子吧(网上有文章号称 5G基站能覆盖20公里,到了那个距离,带带还剩多少,还有什么使用价值,有良心的工程师自己知道),这其实比WIFI的覆盖范围也多不了多少了。面积与覆盖半径的平方成正比,这就意味着5G基站的数量和密度可能是4G的5到10倍,这正是设备商努力鼓吹5G的原因了,可是那么密集的基站,手机即使很低的移动速度,也会频繁切换,而切换又会产生很多的延迟和中断,这些问题又怎么解决?这么多的技术缺陷,注定5G如同无线输电一样,描绘的很好,却是个迟早都会破的大泡沫。

11、再说物联网,物联网是5G炒作者最重要的支撑基础,什么万物互联,物物互联。通信说白了,最重要的指标无非就是延迟、带宽和丢包率,这三个指标是基本指标,光通信这三个指标都远优于5G,在这方面有天生的优势,可以说已到了理想化的程度,对5G来说,那是一道永远也不可能突破的墙,现在5G到处宣传,给人感觉好像是什么划时代的技术,可以轻松超越光通信。WIFI加光宽带足以实现物联网的需要,没有WIFI的地方还可以用4G,没有5G物联就实现不了啦,不会吧,不要尽搞些新概念吓唬人好不好。网上有人说5G的频谱利用率有多高,就这一点足以说明5G是个好技术,请问5G提升频谱利用率的最终目的不就是在有限的频谱内提升带宽吗?可是如果连提升带宽都变得没有意义了,那提高频谱利用率的价值何在呢?

12、有人说楼主不懂,服务器和电脑不一样,服务器里面的硬盘早就使用光纤连接了,但是光纤连接的仍然是硬盘啊,硬盘的速度是瓶颈,你用什么连接速度也上不去,这就是木桶理论了。实际上,现在使用的服务器大多仍是PC机,只是PC机集群而已,但也有极少量的小型机,硬盘仍是最主要的存贮介质,你说可以搞个缓存,那样存取速度就会提高,但那毕竟是极少量的,缓存的容量跟硬盘怎么比啊,那是成千上万倍的差距啊,没有大规模推广的技术,证明是没有商业价值的。

13、有人又说到AR,VR,反正这些技术还没走进人们的生活,大家但吹无妨,这两种技术可能需要三十多兆的带宽,4K高清需要16兆带宽,也就是这样了,这也不需要10G带宽啊,有人又说了,基站的带宽是大家共享的,你以为光宽带的带宽就不是共享的吗?这种AR,VR要戴个大眼镜捂住双眼,你不怕在外面走路会撞电线杆吗?这AR,VR恐怕还只能在家里用用吧,在家里用当然是WIFI和光宽带更好了,5G信号要穿透家里的墙,那丢包率和延迟还不得蹭蹭的往上窜啊。回到物联网的问题,物联网所连的物,绝大部分应该还是在室内吧,室内当然是光宽带和WIFI的地盘了,5G信号穿墙到室内,那信号还不知道差到哪里去了?

14、通信行业这些年的走向,让人摸不着头脑了,就说SDH,明明在延迟、带宽、丢包率三个指标上都远优于PTN,也远优于IP网,却被说成是淘汰技术,许多地方拆了SDH上PTN,结果E1线路经常帧失步,你说这不是在开历史的倒车么?这说明一些人的确可以颠倒黑白,指鹿为马啊?我们还要被那些所谓的技术专家糊弄到哪里去?这已经到了对那些技术专家万分警惕的时刻了,弄不好就会被他们带进坑里去了。

网名为“winnyterry”的业内人士:

唉,再见文科生另一神论,其实论调和理据和上一篇一样,只是写得长了一点。回复较为费时。

首先,这篇神论第一段的逻辑就有问题:楼主的逻辑是:任何技术的发展,都必须先有大需求,再去发展技术。否则就是冒天下之大不讳。但我回忆了一下中学小学学过的中国五千年,和世界近两百年的发展。中国和世界都是在矛盾和失败中前行。人类的每一个成功的发明,一开始的需求其实都和移动通信技术很相似,需求不明显,但发明出来后,这个需求就极之可能变种成爆发式的增涨。

其次,楼主纠结国内为什么看好5G,我想说,移动通信技术从电报通信开始,一直到现在的4.5G,就不只是国内的事,而是全世界的事。5G技术也不只是国内在准备,全世界都在准备,看不看好另说。但如果全世界都5G,就中国4G,我就一定不看好了。中国从电报通信到现在,一直在全球通信产业中都处于模仿跟进付专利费的地位,这个局面直到3G开始有了松动,4G开始有点点的话语权,再到5G话语权又会再增加一点。这种进步对于中国通信产业的意义并不是4G有300Mb/s,5G有1Gb/s这么简单的对比可以概括的了。

再次,wifi与5G对比的问题。坦白说,wifi通信和移动数据通信其实分属于两种不同的群组技术,wifi是计算机通信群组技术,5G是传统移动数据通信技术。这两种技术从2G时代起就一直在对抗,希望获得移动通信的话语权。但自己从传统移动通信技术不停的更新迭代,wifi的速率和容量优势早已不复存在。再加上现在全球特别是中国的通信资费以火箭速度下降,wifi注定在未来是小众应用,只剩下企业内网和大型mall用于宣传之用。根本不可能再回复在2G、3G时代的火爆。可以预料,在5G时代,wifi衰落的速度会更快。作为我本人,从2008年起就很少用到wifi了,除非我所在的那个地方完全没有3、4G信号。但这基本上在我全国跑的经历看,这不可能出现。

最后,我实在无法理解楼主说的:“请问5G提升频谱利用率的最终目的不就是在有限的频谱内提升带宽吗?可是如果连提升带宽都变得没有意义了,那提高频谱利用率的价值何在呢?”再次强调,频谱利用率的要考虑是主要是如何在有限的无线频谱下,容纳更多的并发数据。这点比速率的提升更重要。如果GSM一个载扇能容纳200个客户在线,但WCDMA一个载扇能容纳400个客户在线并能保持超过2G的速率,FDD一个载扇能容纳800个客户在线并能保持3G的速率……你还会认为频谱提升没有意义吗?!

网名为“jeffyko”的业内人士:

个人角度,看5G这个问题,如下几点:

(1)相比4G,物理层上没有重大突破,速率的增加无非是通过更高阶mimo更大带宽来实现,

(2)时延上的改进一是架构的优化,二是芯片技术的提高

(3)iot/mtc未来可能会有很大应用价值,毕竟与每个人的生活息息相关

(4)如楼主所述,路铺的宽又如何,应用在哪里?

所以,不要把5g吹的太悬,最终它也要走下神坛。如同volte一样

网名为“winnyterry”的业内人士:

唉,又见文科生神论!通篇的着眼点只有:速度。太片面了。每一代移动通信技术的革新,其它有三点是最重要的,按重要程度排序是:频谱利用率,时延,网速。

频谱资源不论什么时代在移动通信领域都是最为稀缺的,如何在有限不可再生的无线频谱下,容纳更多的并发数据,这是每一代移动通信进步最重要的出发点。特别是物联网时代,万物互联,频谱利用率不提高,一切都是空谈。

其次是时延,现在4g时延基本上能在20毫秒内,对于打游戏,是够了,但如果是VR应用和智能导航呢,又太慢了,测试要在8毫秒内,智能导航才不会错过执行指令的最佳时点。而5g据说时延能控制在5毫秒内。

而网速仅仅是每一代移动通信技术中重要性排在第三位的,可以说只是在提高频谱利用率和降低时延时顺便提高一下而已。对于用户而言,网速的感知最为明显,运营商设备商的宣传着墨于此不难理解,而前面的两点对于用户的感知远远没有第三点的网速明显,但切勿因此就认为移动通信技术就只是网速的提高,这样就大错特错了。

网名为“heipang”的业内人士:

有人说楼主是文科生观点,其实很多通信业内从业多年的人也会有类似观点。说几个自己的想法:

1. 视频是推动宽带发展的需求,但真的作用有限,有些厂商在大肆鼓吹视频和IPTV,其实无非是想多卖设备带宽,体验、运维这些问题在IP网络里没有提供有效的方案解决。

2. 有些需求真的是我们普通上班族体验不到的,每天早出晚归,三四十岁的人了,哪有时间玩游戏,VR/AR这些将来一定对网络的质量要求很高,也许要等到它们渗透到每个生活的角落,你才能感受到。

3. 物联网,个人觉得在部分场景里用处比较大,比如工业、农业、娱乐园区里,家里那点东西真的需要都联网么,有那个精力多陪孩子读点书吧。

4. 法律法规的建设如果不完善,大家怎么放心去联网,家里装个IP摄像头被入侵了咋办,放在云上的视频被流出怎么办:)当然这个担心有点多余,我们的发展模式一直都是野蛮生长再逐步约束。

网名为“risse”的业内人士:

不知道楼主为何这么抵触。带宽大延迟低,不好吗?难道想回到56K的时代?一张图片显示出来都要10来秒!

标清、高清、4K的视频真的是有很大的差别,不要看到现在网站上的在线播放视频,那些都是压缩了的,只是分辨率没有变而已,有机会你下载北京烤鸭完整版的4K视频看看,就知道区别了。

看了你所说的那么多,感觉你就像老一辈的那种没技术的技术而已。

时代的进步,离不开网络的发展,2G的时候你用手机玩游戏?3G的时候你用手机看视频?4G的时候这些都有了吧!5G的时候就不是简简单单的这些应用了。

网名为“jiu_mao”的业内人士:

各位,技术发展和业务的发展是相辅相成的,没有业务强有力的驱动,不会这么快产生一代又一代的通信技术的。5G也是如此,相信现在有很多炒作的概念,但我觉得更主要的动力还是来自于整个社会的进步、人们更高的需求。

楼主,从你鼓吹5G不如wifi加光宽带这一点来看,我觉得你可能是资深的工程师,但绝不会是移动通信相关的工程师!

网名为“frewave”的业内人士:

3GPP定义的5G最大速度是20G,这是指整个基站的速率,分配到个人的没那么多,5G的进步是通过新的技术极大提升频谱效率和基站容量,对于现有的技术,运营商为何不敢像固网那样包月?到了5G极快的的速率,低廉甚至包月的流量,极大的扩展了移动互联网的使用范围(包括万物互联),这些比固网更便利,基础设施上去了,应用才能推广,在2G时代,不可能有微信这类的应用。对于VR、AR这些应用,就像3G时代的智能手机,只有硬件条件到了,才会推广应用。4K的VR只是入门,楼主说的十几兆就够了,这是经过200倍的视频压缩比才能到的,在对延迟不敏感的视频场景问题不大,对直播或游戏就有问题了,这也是为何HTC的VR要有线连接。一两年后8K的VR才是基本配置。

楼主可能只是做固网的,对移动通信的理解还是不深!

网名为“lovebugzhang”的业内人士:

我觉得楼主还是有很多地方没理解上一贴那么多人反驳你的地方,特别是对频谱利用率,还有服务器的传输速度的理解有点问题。5G有很多优势,,低延迟只是其中之一,而且你逻辑并不清晰,低延迟是优势,自动驾驶是应用,不可混为一谈.你不可能因为自动驾驶这个应用距目前太遥远,就认为整个5G是忽悠的.

网名为“winddt”的业内人士:

立场影响观点。

在4G时代,就有wifi+光取代LTE的说法,结果大家都清楚了,各自都发展的很好。这个时代是你可以发展的很好,但你的光芒不是一定要让别人暗淡无光。

5G时代,wifi+光本就已经纳入5G系统的一部分了,这个在去年的IEEE802会议上已经确定了,以前还可以说lte和wifi两者互为补充,5G时代移动宽带和无线宽带深度融合,均成为整个5G系统的的重要组成部分。

网名为“cano”的业内人士:

之前在公众号上看过一篇关于5G的批评文章,有理有据,深以为然。这次本来是抱着寻找同道中人的念头入坑,没想到大失所望。

拜读了LZ第一篇文章之后,我就有了一个猜测,也闻到了一股熟悉的味道。在读了第二篇后,我觉得,我的猜测和那股味道终于被证实了。

楼主应该是搞固网接入和光通信的吧,从这两篇中大谈ISDN、ADSL、EPON、GPON、STN、PTN这些就能看出来。另外,楼主应该是北邮某教授的铁粉吧,第一篇中只是闻到的味道,第二篇中直接就点出了,呵呵。不过自从在某教授狂喷3G的文章中发现一个初中生都不会错的大漏洞后,我和某教授的文章基本就拜拜了。

我觉得LZ对于无线通信的基础知识,对于5G的基本目标和场景都缺乏最基本的认识。

有线通信和无线通信最大的不同在于信道。有线通信可以毫不费力的搞到一组正交信道,无线就要头拱地;有线通信发现一根管子不够了,再加一根就是了,无线还是头拱地……

对于5G的三大场景,或许那些回复也没说清楚,还请LZ自己回去看看。另外,请楼主注意,现在4G的基站也是对用户数和并发业务数有限制的,而且没你想象的那么多。