写了多年程序看了不少开源代码, 大多数时候都是将知识存于脑中,偶尔做做简单笔记, 就从这里开始写点详细的技术文章吧.
以前做实时流媒体开发时就想对p2p技术做一些研究, 但是因为公司的流媒体技术使用relay服务器中转的模式, 所以一直没有深入研究, 其实实时流媒体占用流量太大而且实时性太强, 特别对于实时视频使用p2p技术并没有太多用处或者说难度太大. 目前区块链技术正处于热门风口,而且其底层也是基于p2p技术上实现, 所以又重拾兴趣研究一番.
比特币的p2p技术好像比较简单,而且是基于c++, 所以我不是很感兴趣, 虽然我以前主要就是使用c++, 但实在是有点厌烦c++的繁琐, 而以太坊有go语言实现, 其p2p是基于kad技术上修改而来,相对比特币的p2p技术更加先进, 刚好我也对golang产生兴趣所以打算下载代码加上偷懒搜索别人网上写的代码分析研究一下具体的实现,同时也可以学习golang的用法, 不过搜索了半天都没有找到别人写的以太坊源码详细分析文章, 于是自己动手分析, 这样理解的也更加深刻.
此源码分析基于: go-ethereum 1.8.1版本
go ethereum的源码结构还是比较清晰, p2p模块单独就在源码的p2p目录下, 入口文件是server.go, 所以就从server.go文件一步一步开始分析吧.
server.go
Server struct
继承了Config struct
, 当调用者创建Server
实例时, 传入Config
配置. Config
的字段使用了toml tag
, TOML是一种配置文件格式, TOML被设计为可以无二义性的转换为一个哈希表(Hash table), 具体的使用可以自行搜索资料, 这里不再深入探讨.
一切从Start()方法开始:
func (srv *Server) Start() (err error) { //wbt p2pserver入口
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.running {
return errors.New("server already running")
}
srv.running = true
srv.log = srv.Config.Logger
if srv.log == nil {
srv.log = log.New()
}
srv.log.Info("Starting P2P networking")
// static fields
if srv.PrivateKey == nil {
return fmt.Errorf("Server.PrivateKey must be set to a non-nil key")
}
if srv.newTransport == nil {
srv.newTransport = newRLPX
}
if srv.Dialer == nil { //tcp客户端连接
srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
}
srv.quit = make(chan struct{})
srv.addpeer = make(chan *conn)
srv.delpeer = make(chan peerDrop)
srv.posthandshake = make(chan *conn)
srv.addstatic = make(chan *discover.Node)
srv.removestatic = make(chan *discover.Node)
srv.peerOp = make(chan peerOpFunc)
srv.peerOpDone = make(chan struct{})
var (
conn *net.UDPConn
sconn *sharedUDPConn
realaddr *net.UDPAddr
unhandled chan discover.ReadPacket
)
...
}
从上面代码可以看出用锁和running标志来保证Start方法只能执行一次。
newTransport是传输协议,使用RLPX加密协议,这也是跟其他kad网络主要不同的地方,后面会讲到,暂时先不用管它。
Dialer为TCPDialer。是Tcp客户端,负责连接别的节点。
下面一堆chan在后面会逐步遇见。
未完代续...