GOLANG实现类似C++模板,返回符合类型的对象
原文参考:https://gocn.io/article/319 在协议解析中,C++的模板有比较大的作用,有时候我们希望丢弃所有的包,只留下特定类型的包。参考SRS的代码SrsRtmpClient::connect_app2: 类型系统的设计, class SrsPacket;
class SrsConnectAppResPacket : public SrsPacket
协议栈提供了 SrsCommonMessage* msg = NULL;
SrsConnectAppResPacket* pkt = NULL;
if ((ret = protocol.expect_message<SrsConnectAppResPacket>(&msg,&pkt)) != ERROR_SUCCESS) {
return ret;
}
SrsAmf0Any* data = pkt->info->get_property("data");
SrsAmf0EcmaArray* arr = data->to_ecma_array();
SrsAmf0Any* prop = arr->ensure_property_string("srs_server_ip");
string srs_server_ip = prop->to_str();
在向服务器发送了 在GOLANG中,也需要定义个interface,参考Packet,当然也是有 type Message struct { Payload []byte }
type Packet interface {} // Message.Payload = Packet.Marshal()
type ConnectAppResPacket struct { Args amf0.Amf0 }
第一种方法,协议栈只需要收取Message,然后解析Message为Packet,收到packet后使用类型转换,判断不是自己需要的包就丢弃: func (v *Protocol) ReadMessage() (m *Message,err error) func (v *Protocol) DecodeMessage(m *Message) (pkt Packet,err error)
不过这两个基础的API,User在使用时,比较麻烦些,每次都得写一个for循环: var protocol *Protocol
for {
var m *Message
m,_ = protocol.ReadMessage()
var p Packet
p,_ = protocol.DecodeMessage(m)
if res,ok := p.(*ConnectAppResPacket); ok {
if data,ok := res.Args.Get("data").(*amf0.EcmaArray); ok {
if data,ok := data.Get("srs_server_ip").(*amf0.String); ok {
srs_server_ip = string(*data)
}
}
}
}
比较方便的做法,就是用回调函数,协议栈需要提供个 func (v *Protocol) ExpectPacket(filter func(m *Message,p Packet)(ok bool)) (err error)
这样可以利用回调函数可以访问上面函数的作用域,直接转换类型和设置目标类型的包: var protocol *Protocol
var res *ConnectAppResPacket
_ = protocol.ExpectPacket(func(m *Message,p Packet) (ok bool){
res,ok = p.(*ConnectAppResPacket)
})
if data,ok := res.Args.Get("data").(*amf0.EcmaArray); ok {
if data,ok := data.Get("srs_server_ip").(*amf0.String); ok {
srs_server_ip = string(*data)
}
}
这样已经比较方便了,不过还是需要每次都给个回调函数。要是能直接这样用就好了: var protocol *Protocol
var res *ConnectAppResPacket
_ = protocol.ExpectPacket(&res)
if data,ok := data.Get("srs_server_ip").(*amf0.String); ok {
srs_server_ip = string(*data)
}
}
这样也是可以做到的,不过协议栈函数要定义为: func (v *Protocol) ExpectPacket(ppkt interface{}) (err error)
在函数内部,使用reflect判断类型是否符合要求,设置返回值。代码参考ExpectPacket,下面是一个简要说明: func (v *Protocol) ExpectPacket(ppkt interface{}) (m *Message,err error) {
// 由于ppkt是**ptr,所以取类型后取Elem(),就是*ptr,用来判断是否实现了Packet接口。
ppktt := reflect.TypeOf(ppkt).Elem()
// ppktv是发现匹配的包后,设置值的。
ppktv := reflect.ValueOf(ppkt)
// 要求参数必须是实现了Packet,避免传递错误的值进来。
if required := reflect.TypeOf((*Packet)(nil)).Elem(); !ppktt.Implements(required) {
return nil,fmt.Errorf("Type mismatch")
}
for {
m,err = v.ReadMessage()
pkt,err = v.DecodeMessage(m)
// 判断包是否是匹配的那个类型,如果不是就丢弃这个包。
if pktt = reflect.TypeOf(pkt); !pktt.AssignableTo(ppktt) {
continue
}
// 相当于 *ppkt = pkt,类似C++中对指针的指针赋值。
ppktv.Elem().Set(reflect.ValueOf(pkt))
break
}
return
}
遗憾的就是这个参数ppkt类型不能是 var res *ConnectAppResPacket
// 这是正确的做法,传递res指针的地址,相当于指针的指针。
_ = protocol.ExpectPacket(&res)
// 这是错误的做法,会在ExpectPacket检查返回错误,没有实现Packet接口
_ = protocol.ExpectPacket(res)
用起来还不错。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- VB.net中的DataGridView获取当前被选中的行号
- kuangbin专题七 POJ3468 A Simple Problem with Integers (
- 如何保护delphi应用程序的源代码?
- Delphi中的C#/ Java“Try/Finally/Catch”等效构造
- Delphi 6 TWinControl后代的WndProc()有时会在主VCL线程中执
- 最近在编写一些bat工具时,发现自己对lua的io和正则表达式使
- k8s与监控--解读prometheus监控kubernetes的配置文件
- GOLANG如何并发
- 如何阅读别人的代码1
- echo安装失败,提示unrecognized import path "golang.