40块钱解决IPTV内网融合

起因

去年的这个时候,解决了家里组网的问题:《我的家庭AC+AP分体组网高性价比满血方案》。最近,由于家里要看直播的需求开始骚动,于是把目光投向了电信的IPTV。

之所以选择运营商的IPTV,主要是因为互联网三方直播走的是公网,遇到节假日直播,非常容易卡顿。而运营上IPTV具有独立带宽、vlan专网、不占用互联网带宽、有4K信号源等优点,使用感受上非常稳定,尤其是看世界杯直播、晚会直播有相当优势。

基础知识

运营商的IPTV直播一般使用igmp 组播协议。认证上,主要有两种,一种是电信机顶盒上PPPOE拨好,一种是IPoE, 也就是DHCP+r认证:

DHCP+OPTION扩展字段进行认证,又称为IPoE认证方式。用来作为DHCP扩展的OPTION字段主要为OPTION60和OPTION82。其中,OPTION60中带有Vendor和Service Option信息,是由用户终端发起DHCP请求时携带的信息,网络设备只需要透传即可。其在应用中的作用是用来识别用户终端类型,从而识别用户业务类型,DHCP服务器可以依赖于此分配不同的业务IP地址。OPTION82信息是由网络设备插入在终端发出的DHCP报文中,主要用来标识用户终端的接入位置,DHCP OPTION82信息可以由DHCP SNOOP-ING或DHCPRELAY设备进行插入。

成都地区是第二种认证方式,也是当前把IPTV融合到内网最简单的方式。

网络结构及设备

家里设备挺多,但是结构尽量保持简单:光猫桥接,在主路由A进行拨号;电信IPTV直插电信盒子。主路由A下面挂了两个10口千兆交换机,且都不支持vlan, 两个交换机之间通过光纤连接。弱电箱空间足够,且网线预留充足,因此就不考虑vlan单线复用方案了。

从海鲜市场40包邮购入了一个今天主角——新路由mini,并刷入padavan作为路由器B待接入内网。

IPTV内网融合设置方法

  1. 光猫IPTV的LAN口接入独立的路由器B的WAN口。路由器B只有个作用:接收组播的IPTV数据,然后将它通过udprxy转为http供内网客户端访问。路由器
  2. 设置路由器B的WAN口。因为IPTV的LAN口是组播,成都地区IPTV虽然采用了IPoE认证,但是因为我们不需要看回放,因此只需要随意设置一个IP即可,注意改IP不要跟主路由A在同一个网络。
  3. 路由器B的LAN口接入路由器A的LAN口。设置路由器B的LAN地址为主路由A的某地址,这里我设置为 192.168.10.252。关闭路由器B的DHCP功能,避免跟主路由A的DHCP冲突。
  4. 开启路由器B的udprxy,设置端口 8848。 以成都地区为例,在VLC上就可以通过 http://192.168.10.252:8848/rtp/239.93.1.12:2224 观看CCTV-5的直播了。
  5. 开启主路由器xUPNPd, 然后上传网友分享的m3u节目表单,实现在内网任何设备通过DLNA共享节目列表的功能。

使用感受

  1. 因为使用路由器B进行了udp转http到内网,因此可以完全在内网关闭组播协议,IPTV融合到内网后,对内网没有任何影响。
  2. 如果想保留电信的IPTV LAN口,可以把光猫上的LAN口多绑定一个到光猫的IPTV配置上(一般为 _OTHER_B_VID_XX)。
  3. 便宜购买的新路由mini价格便宜,但是一个百兆路由。然后,实际测试下来,发现1路1080P占用带宽约 10mbps, cpu占用20%,也就是说同时播放5路1080P已经达到CPU极限,但是带宽仅占用到50mbps。一路4K占用带宽 25mbps, cpu占用 60%, 同样也是先达到CPU极限。完全不用纠结百兆路由带宽的问题。
  4. 家里宽带使用的是电信。4K信号源还是比较少,目前使用下来CCTV的奥运频道4K无论是内容还是信源都是最值得观看的。

Golang http response 解压缩分析

Golang自带的 http 标准库一直是自己的首选http library,最主要的原因是对标准的细节支持非常到位,兼容性优秀。

前几天遇到一个问题一直没时间处理,晚上花了挺多时间排查,落实到代码上其实也就一行的修改量,但是对于有段时间没有读标准库代码的自己来说倒是值得记录一下。

问题

使用标准库进行 http 请求时,如果在请求 header 中手动设置了 Accept-Encoding, 那么返回的响应内容看起来是乱码(其实是二进制)。

原因

定位的过程其实挺曲折的,分析抓包的时候对比了多个请求数据,最终才确认是header 中的 Accept-Encoding 导致。而这个细节之前在 http.Transport 的 DisableCompression 字段是有文档说明的:

也就是说:

  • 如果设置 DisableCompressiontrue 并且请求中没有设置 Accept-Encoding 的情况下,那么将发送要求服务器返回非压缩的请求;如果 DisableCompressionfalse (默认值), 则会发送允许服务器返回压缩内容的请求,并且会在 transport 层自动把 response 内容解压返回到业务层。这一点很容易理解,屏蔽了解压读取压缩内容细节,使用起来也非常方便。这也是我对golang http 标准库的理解,很多东西都自动处理了。我遇到的问题在于下面说的第二点。
  • 如果用户手动在请求中设置了 Accept-Encoding, 那么标准库会认为业务层是想自己接管 response 内容的处理,因此会返回原始的未解压的内容。也就是上面问题里面提到的乱码数据。

这段话看起来比较绕,我们直接看一下 transport.go 相关代码。

发起请求时,DisableCompressionfalse 且业务层没有设置 Accept-Encoding 的情况下,自动设置header:

读取 response 时,解压内容:

小结

很多问题其实都是魔鬼在细节中,本质上还是基础文档没有仔细阅读。不过找问题的晚上其实也挺开心的,因为通过配置好的 github Copilot 来写代码,整个过程真的非常愉悦。最开始自己还会写几个关键字,引导 Copilot 给出提示代码,后面索性回车就开始等待 Copilot 帮我写代码,跟自己编写代码匹配度能到 80% 以上。目前已知的不足是在 string 的内容推导上有时候不太准确(事实上,人工编码的时候,这部分也容易出现typo或者其他错误),因此要注意这部分代码的review.

而立之年的妥协

生活有时候非常神奇,在特定的时间点会递上特殊的经历。而在自己身上,则是非常戏剧的两年。

去年前的今天,蚂蚁按下了暂停键,当时的自己在楼下看着logo上的霓虹灯,心中毫无波澜,心里想的是今日今岁不唐捐。

今年的今天,我接受了外部的和解,也跟自己做了妥协。我知道我内心并不接受这样的结果。但是,所谓妥协,跟接受和认同无关,只是学会了和解自己,也不再为难他人。

我不知道这两年生活在这两个同一时间点上的经历是礼物还是考验。几天前,脑子里总会浮现出屈原的一些词句,放弃了回忆当时那几句话在课本里面备注的含义,却很自然的理解了那些诗句的感受和含义。

我不知道什么时候才能消解内心深处的焦虑与愤怒,但是我希望自己在越来越车轻路熟处理眼下事情的时候,能保持对自己的持续感受和鞭策。

从今以后,我出发的地方,已无法归来。

可至今日,已是一种眷顾与幸运。

远去,不归。