2020年六月月 发布的文章

亲爱的母校哈工大,100岁生日快乐!

img{512x368}

今天是我的母校哈尔滨工业大学百年校庆的正日子(1920.6.7~2020.6.7),这里祝亲爱的母校哈工大,100岁生日快乐!

img{512x368}

图:哈工大百年生日快乐!

今年春节前,大学班级群里已经开始策划“百年校庆,重归母校”的活动了。由于毕业后还没有机会回母校看看,因此我是十分渴望和同窗四年的兄弟姐妹们一起再回母校追寻曾经的大学校园记忆的。

img{512x368}

图:曾经青涩的我们

但不巧的是,新冠疫情爆发,百年校庆也改为云校庆了。重游母校的愿望泡汤,但这丝毫并不影响我们对母校的祝福!

img{512x368}

图:规格严格,功夫到家

本周五,我刚刚从校友手中拿到母校寄发过来的百年校庆纪念徽章,十分欢喜,作为哈工大人,我十分自豪。

img{512x368}

图:百年校庆纪念徽章1

img{512x368}

图:百年校庆纪念徽章2

本想在这里写点关于母校原创的内容,但思来想去发现自己对母校历史和毕业后的学校发展的了解还是十分有限的:(,于是在这里就转发一下来自哈工大官方微博的文章《恢弘百年风华,亲爱的哈工大百岁生日快乐!》,我也和大家一起重温一下百年哈工大的辉煌历程吧!

img{512x368}

一世纪规格功夫, 新百年世界一流 庚子仲夏的明媚阳光里,哈尔滨工业大学迎来了100岁生日

img{512x368}

一百年风华正茂,一百年春华秋实鹏城威海,再到滔滔的松花江畔,一校三区的哈工大人相聚云端,共祝母校百年华诞

img{512x368}

百年岁月,梦想起航

1920年,哈尔滨工业大学的前身:哈尔滨中俄工业学校成立,哈工大百年历史从此刻开始。

img{512x368}

新中国成立后,哈工大被国家确定为全国学习国外高等教育办学模式的两所样板大学之一。1954年,哈工大进入国家首批重点建设的6所高校行列。在五十年代,哈工大就以“工程师的摇篮”享誉全国。

img{512x368}

这一时期,八百余名平均年龄只有27.5岁的青年教师在祖国建设最困难的时刻,怀揣着炽热的报国之心扎根祖国北疆,肩负起教学与科研任务。

img{512x368}

在艰苦磨练下,老一辈哈工大“八百壮士”用不屈的脊梁和坚韧的信念铸就了百年发展的力量源泉。

img{512x368}

1996年, 哈工大进入国家“211工程”首批重点建设高校。
1999年, 被确定为国家“985工程”首批重点建设的9所大学之一。
2000年, 哈工大与同根同源的哈尔滨建筑大学合并组建新的哈尔滨工业大学

img{512x368}

2017年,哈工大入选“双一流”建设A类高校。
在全国第四轮学科评估中,哈工大共有17个学科位列A类。学科优秀率(A类学科占授权学科的比例)位列全国第六位。A类学科数量位列全国第八位。 工科A类数量位列全国第二位。

img{512x368}

中国第一台会下棋能说话的计算机
中国第一颗高校牵头自主研制的小卫星
世界首次人机协同在轨维修技术试验
世界首次揭示艾滋病病毒毒力因子结构
国际首次高轨卫星对地高速
激光双向通信试验
首创世界最大单口径射电望远镜(天眼)
主动反射面结构方案
……

img{512x368}

从中国到世界,一个又一个“第一”在这里诞生,在浩瀚的历史画卷上留下哈工大的印记。

时光荏苒,岁月如歌

一世纪岁月流转, 一百年寒来暑往。

十秩更替,哈工大校园的春花,夏雨,秋月,冬雪都是不曾令人忘却的好风景。

初春,幽幽丁香蹁跹入梦,将这最美好的时光定格在脑海。

img{512x368}

盛夏,虫鸣清脆不绝于耳,灼热的阳光勾勒出一方瓦蓝的晴天。

img{512x368}

金秋,秋高气爽惠风和畅,一地黄叶有着说不出的绚烂。

img{512x368}

寒冬,北国冰城万里雪飘,漫天飞雪见证了光阴的变迁。

img{512x368}

哈工大的校园不仅见证了百年时光荏苒,亦见证了军训时青涩的你,不畏酷暑。

img{512x368}

毕业时成熟的你,神采飞扬。

img{512x368}

实验室里通宵达旦的你,秉承规格严格。

img{512x368}

正心楼内刻苦奋进的你,锤炼到家功夫。

img{512x368}

博士生集体婚礼上甜蜜的你,许下爱情的誓言。

img{512x368}

运动会上英姿飒爽的你,顽强拼搏奋勇争先。

img{512x368}

冰雪节上欢乐的你, 用双手雕刻出一朵朵冰花。

img{512x368}

而现在轮到你,见证这所学校的辉煌。

不忘初心,砥砺奋进

“规格严格,功夫到家”,这略显直白的校训凝聚成哈工大的内在气质,更成为铸就人生规格的一把标尺。

如果“规格”和“功夫”是哈工大人传承百年的立身之本,那么“一寸丹心惟报国” 是他们熔铸于心的无悔承诺。

img{512x368}

我国第一代核武器型号总设计师俞大光院士,在那个艰难的年代克服重重困难,亲手缔造核弹引爆系统,“点燃”了中国第一颗原子弹,让全世界都为之惊叹。

img{512x368}

“两弹一星”元勋、国家勋章获得者孙家栋院士,作为总设计师亲历了“东方红一号”、“探月工程”、“北斗导航工程” 等重大航天工程,为航天事业无悔奉献。 “国家需要,我就去做”, 他用一生践行了这句对祖国的诺言。

img{512x368}

2018年度国家最高科技奖获得者、两次获得国家技术进步一等奖刘永坦院士,用40年的坚守和专注带出一支驻守北国边疆的“雷达铁军”。耄耋之年,他依旧默默耕耘,是当之无愧的“中国脊梁”。

img{512x368}

中国第一任核潜艇总设计师、中国著名的核动力专家彭士禄院士,身上有股“孺子牛”的犟劲。不做则已,一做到底。从核潜艇到核电站,他一生干的这两件大事都是从零起步,用坚韧的信念书写不平凡的传奇。

img{512x368}

中国绕月探测工程前总指挥 栾恩杰院士
中国第三任核潜艇总设计师、 中国船舶总体和动力专家 张金麟院士
中国航空发动机专家、 太行发动机总设计师 张恩和校友
……

一个个在历史中闪耀着光辉的名字从这里开始征程。

img{512x368}

建校百年历史里,哈工大为祖国培养了30余万优秀人才。他们或隐姓埋名,或执着耕耘。一生孜孜探索只为祖国更加繁荣昌盛。

img{512x368}

百载光阴流转,不忘峥嵘岁月,风雨兼程。

铭记责任,竭诚奉献的爱国精神
求真务实,崇尚科学的求是精神
海纳百川,协作攻关的团结精神
自强不息,开拓创新的奋进精神

薪火相传的哈工大精神,在每个哈工大人心中生根发芽!

img{512x368}

如潮思绪,千言万语,都化作对母校最真挚的祝愿:“亲爱的哈工大,100岁生日快乐!”

注:校庆云直播地址:https://live.bilibili.com/5567237


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

关于xml包在Unmarshal时将\r\n重写为\n的问题

今年4月份,中国移动、中国电信、中国联通三大运营商联合举行线上发布会,发布了《5G消息白皮书》。所谓5G消息,即传统短信消息(仅能进行文本展示)的升级版,是由GSMA组织制定的RCS(Rich Communication Suite)消息规范所定义。2019年RCS UP(unified profile)更新到2.4版本,并成为了5G终端标准的一部分,该版本也是第一个具备商用能力的版本,为5G消息商用奠定了基础。中国移动计划2020.6月末正式实现5G消息的商用,目前已经在浙江和广东建立了两个5G消息的支撑节点(分别由中兴和华为承建)。作为电信移动增值领域的厂商,我方也参与了与浙江节点进行行业5G消息平台(MaaP)联调与应用开发。

这引子有些长,本文重点不在5G消息,而在于与行业5G消息平台对接时遇到的一个Go xml包的问题,这是记录一下,以供自己备忘,同时也供广大gopher们参考。

1. 问题现象

行业5G消息使用的通信协议本质上就是xml over http(s)。在http Body的xml中,有一个字段bodyText承载了真正到达5G智能终端上的有效信息载荷,且这个字段是一个CDATA包裹的字段。在我们系统的某个转发流程中,我们解析了从Chatbot(5G行业消息机器人)下发的行业5G消息,但我们发现解析后的bodyText字段中的“\r\n”都被转换为“\n”了。我们用一个例子来直观描述一下该问题:

// xml-rewrite-carriage-return/test2.go

package main

import (
    "encoding/hex"
    "encoding/xml"
    "fmt"
)

type DescCDATA struct {
    Desc string `xml:",cdata"`
}

type Person struct {
    Name string    `xml:"name"`
    Age  int       `xml:"age"`
    Desc DescCDATA `xml:"desc"`
}

var profileFmt = `<person>
<name>"tony bai"</name>
<age>33</age>
<desc><![CDATA[%s]]></desc>
</person>`

func main() {
    c := fmt.Sprintf(profileFmt, "hello\r\nxml")
    var p Person
    err := xml.Unmarshal([]byte(c), &p)
    if err != nil {
        fmt.Println("unmarshal error:", err)
        return
    }
    fmt.Println("unmarshal ok")

    fmt.Println(hex.Dump([]byte("hello\r\nxml")))
    fmt.Println(hex.Dump([]byte(p.Desc.Desc)))
}

运行该例子:

$go run test2.go
unmarshal ok
00000000  68 65 6c 6c 6f 0d 0a 78  6d 6c                    |hello..xml|

00000000  68 65 6c 6c 6f 0a 78 6d  6c                       |hello.xml|

这是一个非常简单的xml unmarshal(反序列化)的例子。我们看到反序列化后,结构体desc字段中的内容相比于原始的xml中desc的内容少了一个字符:0x0d,即“\r”(carriage-return)。我们一直以为针对原xml中CDATA包裹的数据内容,xml包在unmarshal时会原封不动的拷贝下来。为什么”\r”字符会被删除掉呢?我们接下来找找原因。

2. 问题原因

Go是开源的编程语言,它最大的优势就是遇到问题后可以直接看Go标准库源码,当然也可以通过调试工具跟踪到标准库源码中。xml包并不复杂,我选择了直接看xml unmarshal代码的方式。在$GOROOT/src/encoding/xml/xml.go(go 1.14版本)中,我们在Decoder的text方法中找到如下几行代码:

// $GOROOT/src/encoding/xml/xml.go

... ...

func (d *Decoder) text(quote int, cdata bool) []byte {

... ...

                // We must rewrite unescaped \r and \r\n into \n.
                if b == '\r' {
                        d.buf.WriteByte('\n')
                } else if b1 == '\r' && b == '\n' {
                        // Skip \r\n--we already wrote \n.
                } else {
                        d.buf.WriteByte(b)
                }
... ...

}

Decoder的text方法是xml unmarshal在解析如下面name字段的值(xxxx)时被调用的:

<name>xxxx</name>

这段代码的逻辑是:将xxxx中的\r重写为\n,如果存在\r\n,则将其重写为\n。并且无论是否是CDATA字段,这块的逻辑均是生效的。比如我们将上面例子中的desc字段改为非CDATA类型:

// xml-rewrite-carriage-return/test1.go

type Person struct {
    Name string `xml:"name"`
    Age  int    `xml:"age"`
    Desc string `xml:"desc"`
}

var profileFmt = `<person>
<name>"tony bai"</name>
<age>33</age>
<desc>%s</desc>
</person>`

func main() {
    c := fmt.Sprintf(profileFmt, "hello\r\nxml")
    var p Person
    err := xml.Unmarshal([]byte(c), &p)
    if err != nil {
        fmt.Println("unmarshal error:", err)
        return
    }
    fmt.Println("unmarshal ok")

    fmt.Println(hex.Dump([]byte("hello\r\nxml")))
    fmt.Println(hex.Dump([]byte(p.Desc)))
}

该例子的输出:

$go run test1.go
unmarshal ok
00000000  68 65 6c 6c 6f 0d 0a 78  6d 6c                    |hello..xml|

00000000  68 65 6c 6c 6f 0a 78 6d  6c                       |hello.xml|

我们看到:非CDATA包裹的数据,其中的”\r\n”也被重写为“\n”了。

关于这个问题,在Go项目issue中也有人提及:https://github.com/golang/go/issues/24426 。从该issue的讨论中看,Go标准库xml包的实现应该还是参考了xml规范中关于line end的描述的:

XML parsed entities are often stored in computer files which, for editing convenience, are organized into lines. These lines are typically separated by some combination of the characters CARRIAGE RETURN (#xD) and LINE FEED (#xA).

To simplify the tasks of applications, the XML processor must behave as if it normalized all line breaks in external parsed entities (including the document entity) on input, before parsing, by translating both the two-character sequence #xD #xA and any #xD that is not followed by #xA to a single #xA character.

上面的英文规范翻译过来大致是:

XML解析的实体通常存储在计算机文件中,为了便于编辑,这些文件被组织成多行。 这些行通常由字符回车(#xD)和换行(#xA)的某种组合分隔。

为了简化应用程序的任务(解析回车和换行的组合),在解析之前,XML处理器必须对输入的外部解析实体(包括文档实体)进行转换使其规范化,转换规则是:将两字符序列#xD #xA以及后面未紧跟#xA字符的#xD字符转换为单个的#xA字符。

3. 解决方法

我们的述求就是对CDATA包裹的文本数据中的”\r\n”不做“重写”处理。我们采用了下面的方案:clone一份标准库中的xml包,将clone版本放入我们自己的项目路径下,然后在clone版本基础上修改Decoder的text方法的实现

// xml-rewrite-carriage-return/xml/xml.go

... ...

func (d *Decoder) text(quote int, cdata bool) []byte {

... ...

                // We must rewrite unescaped \r and \r\n into \n.
                //
                // tonybai change: only rewrite when text is not in CDATA section
                // (https://github.com/golang/go/issues/24426)
                if !cdata && b == '\r' {
                        d.buf.WriteByte('\n')
                } else if !cdata && b1 == '\r' && b == '\n' {
                        // Skip \r\n--we already wrote \n.
                } else {
                        d.buf.WriteByte(b)
                }

....

}

改造后的代码仅对非CDATA数据进行\r\n的重写,而对于CDATA类型数据,则原封不动的解析出来。我们将test2.go改造成使用我们的clone版本的xml包的示例代码:test3.go

// xml-rewrite-carriage-return/test3.go

package main

import (
    "encoding/hex"

    "github.com/bigwhite/xmltest/xml"

    "fmt"
)

type DescCDATA struct {
    Desc string `xml:",cdata"`
}

type Person struct {
    Name string    `xml:"name"`
    Age  int       `xml:"age"`
    Desc DescCDATA `xml:"desc"`
}

var profileFmt = `<person>
<name>"tony bai"</name>
<age>33</age>
<desc><![CDATA[%s]]></desc>
</person>`

func main() {
    c := fmt.Sprintf(profileFmt, "hello\r\nxml")
    var p Person
    err := xml.Unmarshal([]byte(c), &p)
    if err != nil {
        fmt.Println("unmarshal error:", err)
        return
    }
    fmt.Println("unmarshal ok")

    fmt.Println(hex.Dump([]byte("hello\r\nxml")))
    fmt.Println(hex.Dump([]byte(p.Desc.Desc)))
}

运行该示例:

$go run test3.go
unmarshal ok
00000000  68 65 6c 6c 6f 0d 0a 78  6d 6c                    |hello..xml|

00000000  68 65 6c 6c 6f 0d 0a 78  6d 6c                    |hello..xml|

我们看到这次包裹在CDATA中的\r\n没有被重写,我们对xml包的修改是有效的。

4. 小结

XML作为上一代被设计用来传输和存储数据的标记语言格式,在Go中的支持并不完善,关于标准库xml包的issue还有好多处于open状态。在标准库xml包更新较慢的情况下,clone一份xml包并进行定制不失为一种好的折中方法。

本文所涉及源码在这里可以下载。


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

2020年4月8日,中国三大电信运营商联合发布《5G消息白皮书》,51短信平台也会全新升级到“51商用消息平台”,全面支持5G RCS消息。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats