以太坊的peer to peer (go-ethereum/p2p)模块能够让你便捷地在p2p网络上开发任何应用。这个p2p 包采用现代化的模块设计,能够很容易地在其之上扩展自己的额外通信协议。
开始一个p2p服务需要你先从构造一个p2p.Server{}
实例开始:
import "github.com/ethereum/go-ethereum/crypto" import "github.com/ethereum/go-ethereum/p2p" nodekey,_ := crypto.GenerateKey() srv := p2p.Server{ MaxPeers: 10,PrivateKey: nodekey,Name: "my node name",ListenAddr: ":30300",Protocols: []p2p.Protocol{},} srv.Start()
如果你想要在该网络上扩展自己的协议,你需要在p2p.Protocol{}
中传入一个子协议:
func MyProtocol() p2p.Protocol { return p2p.Protocol{ // 1. Name: "MyProtocol",// 2. Version: 1,// 3. Length: 1,// 4. Run: func(peer *p2p.Peer,ws p2p.MsgReadWriter) error { return nil },// 5. } }
- 一个子协议对象被称为
Protocol{}
每次一个能够处理该协议的Peer 发起连接时会用到该对象; - 这个服务在网络上发布的名称;
- 这个协议的版本;
- 这个协议需要依赖的信息数目,因为p2p网络是可扩展的,因此其需要具有能够发送随意个数的信息的能力(需要携带type,在下文中我们能够看到说明),p2p的handler需要知道应该预留多少空间以用来服务你的协议。这是也是共识信息能够通过message ID到达各个peer并实现协商的保障。我们的协议仅仅支持一个
message
(详见下文); - 在你的协议主要的handler中,我们现在故意将其留空。这个
peer
变量是指代连接到当前节点,其携带了一些peer本身的信息。其ws
变量是reader和writer允许你同该peer进行通信,如果信息能够发送到当前节点,则反之也能够从本节点发送到对端peer节点。
现在让我们将前面留空的handler代码实现,以让它能够同别的peer通信:
const messageId = 0 // 1. type Message string // 2. func msgHandler(peer *p2p.Peer,ws p2p.MsgReadWriter) error { for { msg,err := ws.ReadMsg() // 3. if err != nil { // 4. return err // if reading fails return err which will disconnect the peer. } var myMessage [1]Message err = msg.Decode(&myMessage) // 5. if err != nil { // handle decode error continue } switch myMessage[0] { case "foo": err := p2p.SendItems(ws,messageId,"bar") // 6. if err != nil { return err // return (and disconnect) error if writing fails. } default: fmt.Println("recv:",myMessage) } } return nil }
- 其中有且唯一的已知信息ID;
- 将Messages alias 为string类型;
-
ReadMsg
将一直阻塞等待,直到其收到了一条新的信息,一个错误或者EOF
; - 如果在读取流信息的过程当中收到了一个错误,最好的解决实践是将其返回给p2p server进行处理。这种错误通常是对端节点已经断开连接;
- 如果解码出来的信息是
foo
将发回一个NewMessage
并用messageId
标记信息类型,信息内容是bar
;而bar
信息在被对端收到之后将被default
case捕获。
现在,我们将上述的所有部分整合起来,得到下面的p2p样例代码:
package main import ( "fmt" "os" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p" ) const messageId = 0 type Message string func MyProtocol() p2p.Protocol { return p2p.Protocol{ Name: "MyProtocol",Version: 1,Length: 1,Run: msgHandler,} } func main() { nodekey,_ := crypto.GenerateKey() srv := p2p.Server{ MaxPeers: 10,Protocols: []p2p.Protocol{MyProtocol()},} if err := srv.Start(); err != nil { fmt.Println(err) os.Exit(1) } select {} } func msgHandler(peer *p2p.Peer,err := ws.ReadMsg() if err != nil { return err } var myMessage Message err = msg.Decode(&myMessage) if err != nil { // handle decode error continue } switch myMessage { case "foo": err := p2p.SendItems(ws,"bar")) if err != nil { return err } default: fmt.Println("recv:",myMessage) } } return nil }
原文: Peer to Peer
未经允许不得转载。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。