日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

以太坊 p2p Server 原理及实现

發布時間:2025/7/25 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 以太坊 p2p Server 原理及实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以太坊p2p原理與實現

區塊鏈技術的去中心依賴于底層組網技術,以太坊的底層實現了p2pServer,大約可以分為這樣三層。

  • 底層路由表。封裝了kad路由,節點的數據結構以及計算記錄,節點搜索,驗證等功能。
  • 中層peer抽象,message開放發送接口,server對外提供peer檢測,初始化,事件訂閱,peer狀態查詢,啟動,停止等功能
  • 以太坊最上層peer,peerset再封裝,通過協議的Run函數,在中層啟動peer時,獲取peer,最終通過一個循環截取穩定peer,包裝在peerset中使用。

底層路由表

這里簡化問題僅討論Node Discovery Protocol。 這一層維護了一個buckets桶,總共有17個桶,每個桶有16個節點和10個替換節點。 Node放入時先要計算hash和localNode的距離。再按距離選擇一個桶放進去,取的時候逐個計算target和每個桶中對象的舉例,詳細參考closest函數,后面會貼出來。

距離公式滿足:f(x,y)=256-8*n-map(x[n+1]^y[n+1]) 注:n為相同節點數量 map為一個負相關的映射關系。

簡單來說就是相似越多,值越小。細節參考Node.go的logdist函數。 這里需要了解算法Kademlia,

. ├── database.go //封裝node數據庫相關操作 ├── node.go //節點數據結構 ├── ntp.go //同步時間 ├── table.go //路由表 ├── udp.go //網絡相關操作

其中最重要的就是table對象,table公共方法有:

  • newTable 實例創建
  • Self local節點獲取
  • ReadRandomNodes 隨機讀取幾個節點
  • Close 關閉
  • Resolve 在周邊查找某個節點
  • Lookup 查找某個節點的鄰近節點

逐個來分析這些方法:

newTable

  • 1:生成對象實例(獲取數據庫客戶端,LocalNode etc)
// If no node database was given, use an in-memory onedb, err := newNodeDB(nodeDBPath, Version, ourID)if err != nil {return nil, err}tab := &Table{net: t,db: db,self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),bonding: make(map[NodeID]*bondproc),bondslots: make(chan struct{}, maxBondingPingPongs),refreshReq: make(chan chan struct{}),initDone: make(chan struct{}),closeReq: make(chan struct{}),closed: make(chan struct{}),rand: mrand.New(mrand.NewSource(0)),ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},}
  • 2:載入引導節點,初始化k桶。
if err := tab.setFallbackNodes(bootnodes); err != nil {return nil, err}for i := 0; i < cap(tab.bondslots); i++ {tab.bondslots <- struct{}{}}for i := range tab.buckets {tab.buckets[i] = &bucket{ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit},}}
  • 3:將節點放入到桶里,生成一條協程用于刷新,驗證節點。
tab.seedRand()tab.loadSeedNodes(false) //載入種子節點// Start the background expiration goroutine after loading seeds so that the search for// seed nodes also considers older nodes that would otherwise be removed by the// expiration.tab.db.ensureExpirer()go tab.loop()

載入種子節點

func (tab *Table) loadSeedNodes(bond bool) {seeds := tab.db.querySeeds(seedCount, seedMaxAge)//數據庫中的種子節點和引導節點合并seeds = append(seeds, tab.nursery...) if bond {seeds = tab.bondall(seeds) //節點驗證}for i := range seeds {seed := seeds[i]age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }}log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)tab.add(seed) //節點入桶}}

節點入桶,同時也要檢查ip等限制。

func (tab *Table) add(new *Node) {tab.mutex.Lock()defer tab.mutex.Unlock()b := tab.bucket(new.sha) //獲取當前節點對應的桶if !tab.bumpOrAdd(b, new) {// Node is not in table. Add it to the replacement list.tab.addReplacement(b, new)}}

桶的選擇

func (tab *Table) bucket(sha common.Hash) *bucket {d := logdist(tab.self.sha, sha) //計算hash舉例if d <= bucketMinDistance {//這里按算法來看,只要hash前三位相等就會到第一個bucketsreturn tab.buckets[0]}return tab.buckets[d-bucketMinDistance-1]}

Resolve

根據Node的Id查找Node,先在當前的桶里面查找,查找一遍之后沒找到就在周邊的節點里面搜索一遍再找。

// Resolve searches for a specific node with the given ID.// It returns nil if the node could not be found.func (tab *Table) Resolve(targetID NodeID) *Node {// If the node is present in the local table, no// network interaction is required.hash := crypto.Keccak256Hash(targetID[:])tab.mutex.Lock()//查找最近節點cl := tab.closest(hash, 1)tab.mutex.Unlock()if len(cl.entries) > 0 && cl.entries[0].ID == targetID {return cl.entries[0]}// Otherwise, do a network lookup.//不存在 搜索鄰居節點result := tab.Lookup(targetID)for _, n := range result {if n.ID == targetID {return n}}return nil}

這里需要理解的函數是 closest,遍歷所有桶的所有節點,查找最近的一個

// closest returns the n nodes in the table that are closest to the// given id. The caller must hold tab.mutex.func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {// This is a very wasteful way to find the closest nodes but// obviously correct. I believe that tree-based buckets would make// this easier to implement efficiently.close := &nodesByDistance{target: target}for _, b := range tab.buckets {for _, n := range b.entries {close.push(n, nresults)}}return close}func (h *nodesByDistance) push(n *Node, maxElems int) {ix := sort.Search(len(h.entries), func(i int) bool {return distcmp(h.target, h.entries[i].sha, n.sha) > 0})if len(h.entries) < maxElems {h.entries = append(h.entries, n)}if ix == len(h.entries) {// farther away than all nodes we already have.// if there was room for it, the node is now the last element.} else {// slide existing entries down to make room// this will overwrite the entry we just appended.//近的靠前邊copy(h.entries[ix+1:], h.entries[ix:])h.entries[ix] = n}}

ReadRandomNodes

整體思路是先拷貝出來,再逐個桶的抽最上面的一個,剩下空桶移除,剩下的桶合并后,下一輪再抽桶的第一個節點,直到填滿給定數據或者桶全部空掉。最后返回填到數組里面的數量。

// ReadRandomNodes fills the given slice with random nodes from the// table. It will not write the same node more than once. The nodes in// the slice are copies and can be modified by the caller.func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {if !tab.isInitDone() {return 0}tab.mutex.Lock()defer tab.mutex.Unlock()// Find all non-empty buckets and get a fresh slice of their entries.var buckets [][]*Node//拷貝節點for _, b := range tab.buckets {if len(b.entries) > 0 {buckets = append(buckets, b.entries[:])}}if len(buckets) == 0 {return 0}// Shuffle the buckets.for i := len(buckets) - 1; i > 0; i-- {j := tab.rand.Intn(len(buckets))buckets[i], buckets[j] = buckets[j], buckets[i]}// Move head of each bucket into buf, removing buckets that become empty.var i, j intfor ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {b := buckets[j]buf[i] = &(*b[0]) //取第一個節點buckets[j] = b[1:] //移除第一個if len(b) == 1 {//空桶移除buckets = append(buckets[:j], buckets[j+1:]...) }if len(buckets) == 0 {break }}return i + 1}

Lookup

lookup會要求已知節點查找鄰居節點,查找的鄰居節點又遞歸的找它周邊的節點

for {// ask the alpha closest nodes that we haven't asked yetfor i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {n := result.entries[i]if !asked[n.ID] {asked[n.ID] = truependingQueries++ go func() {// Find potential neighbors to bond withr, err := tab.net.findnode(n.ID, n.addr(), targetID)if err != nil {// Bump the failure counter to detect and evacuate non-bonded entriesfails := tab.db.findFails(n.ID) + 1tab.db.updateFindFails(n.ID, fails)log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)if fails >= maxFindnodeFailures {log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)tab.delete(n)}}reply <- tab.bondall(r)}()}}if pendingQueries == 0 {// we have asked all closest nodes, stop the searchbreak}// wait for the next replyfor _, n := range <-reply { //此處會阻塞請求if n != nil && !seen[n.ID] {seen[n.ID] = trueresult.push(n, bucketSize)}}pendingQueries--}

桶的維護

桶初始化完成后會進入一個循環邏輯,其中通過三個timer控制調整周期。

  • 驗證timer 間隔 10s左右
  • 刷新timer 間隔 30 min
  • 持久化timer 間隔 30s
revalidate = time.NewTimer(tab.nextRevalidateTime())refresh = time.NewTicker(refreshInterval)copyNodes = time.NewTicker(copyNodesInterval)

刷新邏輯:重新加載種子節點,查找周邊節點,隨機三個節點,并查找這三個節點的周圍節點。

func (tab *Table) doRefresh(done chan struct{}) {defer close(done)tab.loadSeedNodes(true)tab.lookup(tab.self.ID, false)for i := 0; i < 3; i++ {var target NodeIDcrand.Read(target[:])tab.lookup(target, false)}}

驗證邏輯:驗證每個桶的最末尾節點,如果該節點通過驗證則放到隊首(驗證過程是本地節點向它發送ping請求,如果回應pong則通過)

last, bi := tab.nodeToRevalidate() //取最后一個節點if last == nil {// No non-empty bucket found.return}// Ping the selected node and wait for a pong.err := tab.ping(last.ID, last.addr()) //通信驗證tab.mutex.Lock()defer tab.mutex.Unlock()b := tab.buckets[bi]if err == nil {// The node responded, move it to the front.log.Debug("Revalidated node", "b", bi, "id", last.ID)b.bump(last) //提到隊首return}

Peer/Server

相關文件

. ├── dial.go //封裝一個任務生成處理結構以及三種任務結構中(此處命名不太精確) ├── message.go //定義一些數據的讀寫接口,以及對外的Send/SendItem函數 ├── peer.go //封裝了Peer 包括消息讀取 ├── rlpx.go //內部的握手協議 ├── server.go //初始化,維護Peer網絡,還有一些對外的接口

這一層會不斷的從路由中提取節點,提取出來的節點要經過身份驗證,協議檢查之后加入到peer里面,緊接著如果沒有人使用這個peer,這個peer就會被刪除,再重新選擇一些節點出來繼續這個流程,peer再其中是隨生隨銷,這樣做是為了平均的使用所有的節點,而不是僅僅依賴于特定的幾個節點。因而這里從Server開始入手分析整個流程

Peers() //peer對象PeerCount() //peer數量AddPeer(node *discover.Node) //添加節點RemovePeer(node *discover.Node) //刪除節點SubscribeEvents(ch chan *PeerEvent) //訂閱內部的事件(節點的增加,刪除)//以上四個屬于對外的接口,不影響內部邏輯Start() //server開始工作SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) //啟動一個連接,經過兩次驗證之后,如果通過則加入到peer之中。

Start初始化

Start做了三件事,生成路由表于建立底層網絡。生成DialState用于驅動維護本地peer的更新與死亡,監聽本地接口用于信息應答。這里主要分析peer的維護過程。函數是run函數。

func (srv *Server) Start() (err error) {//**************初始化代碼省略if !srv.NoDiscovery && srv.DiscoveryV5 {unhandled = make(chan discover.ReadPacket, 100)sconn = &sharedUDPConn{conn, unhandled}}// node tableif !srv.NoDiscovery {//路由表生成cfg := discover.Config{PrivateKey: srv.PrivateKey,AnnounceAddr: realaddr,NodeDBPath: srv.NodeDatabase,NetRestrict: srv.NetRestrict,Bootnodes: srv.BootstrapNodes,Unhandled: unhandled,}ntab, err := discover.ListenUDP(conn, cfg)if err != nil {return err}srv.ntab = ntab}if srv.DiscoveryV5 {//路由表生成var (ntab *discv5.Networkerr error)if sconn != nil {ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase)} else {ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, realaddr, "", srv.NetRestrict) //srv.NodeDatabase)}if err != nil {return err}if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil {return err}srv.DiscV5 = ntab}dynPeers := srv.maxDialedConns()//newDialState 對象生成,這個對象包含Peer的實際維護代碼dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)// handshake 協議加載srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}for _, p := range srv.Protocols {srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())}// listen/dial//監聽本地端口if srv.ListenAddr != "" {if err := srv.startListening(); err != nil {return err}}if srv.NoDial && srv.ListenAddr == "" {srv.log.Warn("P2P server will be useless, neither dialing nor listening")}srv.loopWG.Add(1)//重要的一句,開個協程,在其中做peer的維護go srv.run(dialer)srv.running = truereturn nil}

run 開始peer的生成

該函數中定義了兩個隊列

runningTasks []task //正在執行的任務queuedTasks []task //尚未執行的任務

定義了三個匿名函數

//從正在執行任務中刪除任務delTask := func(t task) {for i := range runningTasks {if runningTasks[i] == t {runningTasks = append(runningTasks[:i], runningTasks[i+1:]...)break}}}//開始一批任務startTasks := func(ts []task) (rest []task) {i := 0for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ {t := ts[i]srv.log.Trace("New dial task", "task", t)go func() {t.Do(srv); taskdone <- t }()runningTasks = append(runningTasks, t)}return ts[i:]}//啟動開始一批任務再調用dialstate的newTasks函數生成一批任務,加載到任務隊列里面scheduleTasks := func() {// Start from queue first.queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...)// Query dialer for new tasks and start as many as possible now.if len(runningTasks) < maxActiveDialTasks {nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now())queuedTasks = append(queuedTasks, startTasks(nt)...)}}

定義了一個循環,分不同的chanel執行對應的邏輯

for {//調度開始找生成任務scheduleTasks()select {case <-srv.quit://退出break runningcase n := <-srv.addstatic: //增加一個節點 該節點最終會生成一個dialTask //并在newTasks的時候加入到讀列srv.log.Debug("Adding static node", "node", n)dialstate.addStatic(n)case n := <-srv.removestatic://直接刪除該節點 節點不再參與維護,很快就會死掉了dialstate.removeStatic(n)if p, ok := peers[n.ID]; ok {p.Disconnect(DiscRequested)}case op := <-srv.peerOp:// Peers 和 PeerCount 兩個外部接口,只是讀取peer信息op(peers)srv.peerOpDone <- struct{}{}case t := <-taskdone://task完成后會根據不同的任務類型進行相應的處理srv.log.Trace("Dial task done", "task", t)dialstate.taskDone(t, time.Now())delTask(t)case c := <-srv.posthandshake://身份驗證通過 if trusted[c.id] {// Ensure that the trusted flag is set before checking against MaxPeers.c.flags |= trustedConn}select {case c.cont <- srv.encHandshakeChecks(peers, inboundCount, c):case <-srv.quit:break running}case c := <-srv.addpeer://身份協議驗證通過 加入隊列err := srv.protoHandshakeChecks(peers, inboundCount, c)if err == nil {// The handshakes are done and it passed all checks.p := newPeer(c, srv.Protocols)// If message events are enabled, pass the peerFeed// to the peerif srv.EnableMsgEvents {p.events = &srv.peerFeed}name := truncateName(c.name)srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)go srv.runPeer(p) //觸發事件 此處是最上層截取peer的位置,如果此物沒有外部影響,那么這個peer很快就被銷毀了peerAdd++fmt.Printf("--count %d--- add %d-- del %d--\n",len(peers),peerAdd,peerDel)peers[c.id] = pif p.Inbound() {inboundCount++}}// The dialer logic relies on the assumption that// dial tasks complete after the peer has been added or// discarded. Unblock the task last.select {case c.cont <- err:case <-srv.quit:break running}case pd := <-srv.delpeer://移除peerd := common.PrettyDuration(mclock.Now() - pd.created)pd.log.Debug("Removing p2p peer", "duration", d, "peers", len(peers)-1, "req", pd.requested, "err", pd.err)delete(peers, pd.ID())peerDel++fmt.Printf("--count %d--- add %d-- del %d--\n",len(peers),peerAdd,peerDel)if pd.Inbound() {inboundCount--}}}

記住上面的代碼,再來逐個的看:

scheduleTasks

scheduleTasks調度生成任務,生成的任務中有一種dialTask的任務,該任務結構如下

type dialTask struct {flags connFlagdest *discover.NodelastResolved time.TimeresolveDelay time.Duration}func (t *dialTask) Do(srv *Server) {if t.dest.Incomplete() {if !t.resolve(srv) {return}}err := t.dial(srv, t.dest) //此處會調用到setupConn函數if err != nil {log.Trace("Dial error", "task", t, "err", err)// Try resolving the ID of static nodes if dialing failed.if _, ok := err.(*dialError); ok && t.flags&staticDialedConn != 0 {if t.resolve(srv) {t.dial(srv, t.dest)}}}}

dial最終回調用到setupConn函數,函數只保留重點的幾句,篇幅有點長了

func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) error {//身份驗證碼 獲取設備,標識等信息if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != //此處會往chanel中添加連接對象,最終觸發循環中的posthandshake分支err = srv.checkpoint(c, srv.posthandshake) //協議驗證phs, err := c.doProtoHandshake(srv.ourHandshake)c.caps, c.name = phs.Caps, phs.Name//此處會往chanel中添加連接對象 最終觸發循環中的addpeer分支err = srv.checkpoint(c, srv.addpeer)}

posthandshake 分支僅僅做了驗證,addpeer做的事情就比較多了,重要的就是執行runPeer函數

func (srv *Server) runPeer(p *Peer) {// 廣播 peer addsrv.peerFeed.Send(&PeerEvent{Type: PeerEventTypeAdd,Peer: p.ID(),})// run the protocolremoteRequested, err := p.run() //// 廣播 peer dropsrv.peerFeed.Send(&PeerEvent{Type: PeerEventTypeDrop,Peer: p.ID(),Error: err.Error(),})//移除peersrv.delpeer <- peerDrop{p, err, remoteRequested}}func (p *Peer) run() (remoteRequested bool, err error) {//*************writeStart <- struct{}{}p.startProtocols(writeStart, writeErr)//*************//這一句阻塞性確保了peer的存活p.wg.Wait() }func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) {p.wg.Add(len(p.running))for _, proto := range p.running {proto := protoproto.closed = p.closedproto.wstart = writeStartproto.werr = writeErrvar rw MsgReadWriter = protoif p.events != nil {rw = newMsgEventer(rw, p.events, p.ID(), proto.Name)}p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version))go func() {//其他的都是為這一句做準備的,在以太坊中p2p就是靠這一句對上層暴露peer對象err := proto.Run(p, rw)if err == nil {p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version))err = errProtocolReturned} else if err != io.EOF {p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err)}p.protoErr <- errp.wg.Done() }()}}

這樣就可以可理出一條思路 scheduleTasks執行生成dialTask任務 dialTask任務執行過程中逐個填充posthandshake,addPeer這兩個chanel。 addPeer執行時對上層暴露了Peer對象,完成后填充了delpeer,最后刪除了Peer。

任務的生成

具體看代碼中的注釋

func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {if s.start.IsZero() {s.start = now}var newtasks []task//這里聲明了一個添加任務的函數 addDial := func(flag connFlag, n *discover.Node) bool {if err := s.checkDial(n, peers); err != nil {log.Trace("Skipping dial candidate", "id", n.ID, "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err)return false}s.dialing[n.ID] = flag //排除掉已經再測試的newtasks = append(newtasks, &dialTask{flags: flag, dest: n})return true}// Compute number of dynamic dials necessary at this point.needDynDials := s.maxDynDials //當前系統中最大連接數目for _, p := range peers { //扣除已建立鏈接的peerif p.rw.is(dynDialedConn) {needDynDials--}}for _, flag := range s.dialing { //扣除已建立鏈接的peerif flag&dynDialedConn != 0 {needDynDials--}}//外部命令添加的節點 這種節點不占用needDynDials數目,//是為了保證手動加的節點能夠起效for id, t := range s.static {err := s.checkDial(t.dest, peers)switch err {case errNotWhitelisted, errSelf:log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err)delete(s.static, t.dest.ID)case nil:s.dialing[id] = t.flagsnewtasks = append(newtasks, t)}}// If we don't have any peers whatsoever, try to dial a random bootnode. This// scenario is useful for the testnet (and private networks) where the discovery// table might be full of mostly bad peers, making it hard to find good ones.if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && //檢查引導節點 因為引導節點比搜索到的節點更大概率靠譜 因而比較靠前now.Sub(s.start) > fallbackInterval {bootnode := s.bootnodes[0]s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...)s.bootnodes = append(s.bootnodes, bootnode)if addDial(dynDialedConn, bootnode) {needDynDials--}}//隨機的從路由中抽取最大節點的二分之一randomCandidates := needDynDials / 2if randomCandidates > 0 {n := s.ntab.ReadRandomNodes(s.randomNodes)for i := 0; i < randomCandidates && i < n; i++ {if addDial(dynDialedConn, s.randomNodes[i]) {needDynDials--}}}// 從lookupbuf中抽取i := 0for ; i < len(s.lookupBuf) && needDynDials > 0; i++ {if addDial(dynDialedConn, s.lookupBuf[i]) {needDynDials--}}s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])]// 如果還是不夠,路由再去搜索節點if len(s.lookupBuf) < needDynDials && !s.lookupRunning {s.lookupRunning = truenewtasks = append(newtasks, &discoverTask{})}// waitif nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 {t := &waitExpireTask{s.hist.min().exp.Sub(now)}newtasks = append(newtasks, t)}return newtasks}

消息發送

另一個是message中的Send,SendItem函數 實現了MsgWriter的對象都可以調用這個函數寫入,覺得這里沒什么必要,完全可以封裝到peer里面去,不過它上層做廣播的時候確實是調用的這兩個函數。

func Send(w MsgWriter, msgcode uint64, data interface{}) error {size, r, err := rlp.EncodeToReader(data)if err != nil {return err}return w.WriteMsg(Msg{Code: msgcode, Size: uint32(size), Payload: r})}func SendItems(w MsgWriter, msgcode uint64, elems ...interface{}) error {return Send(w, msgcode, elems)}

以太坊上層調用

Peer/PeerSet

文件:go-ethereum/eth/peer.go

定義了兩個struct,Peer和PeerSet。Peer封裝了底層的p2p.Peer,集成了一些和業務相關的方法,比如SendTransactions,SendNewBlock等。PeerSet是Peer的集合

type peer struct {id string*p2p.Peerrw p2p.MsgReadWriterversion int // Protocol version negotiatedforkDrop *time.Timer // Timed connection dropper if forks aren't validated in timehead common.Hashtd *big.Intlock sync.RWMutexknownTxs *set.Set // Set of transaction hashes known to be known by this peerknownBlocks *set.Set // Set of block hashes known to be known by this peer}type peerSet struct {peers map[string]*peerlock sync.RWMutexclosed bool}

Peer注冊/注銷

文件:go-ethereum/eth/handler.go manager.handle在檢查了peer后會把這個peer注冊到peerset中,表示此peer可用,發生錯誤后peerset注銷該peer,返回錯誤,最后再Server中銷毀。

manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))for i, version := range ProtocolVersions {// Skip protocol version if incompatible with the mode of operationif mode == downloader.FastSync && version < eth63 {continue}// Compatible; initialise the sub-protocolversion := version // Closure for the runmanager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{Name: ProtocolName,Version: version,Length: ProtocolLengths[i],Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {peer := manager.newPeer(int(version), p, rw)select {case manager.newPeerCh <- peer:manager.wg.Add(1)defer manager.wg.Done()//此處如果順利會進入for循環 如果失敗返回錯誤我會銷毀掉這個peerreturn manager.handle(peer) case <-manager.quitSync:return p2p.DiscQuitting}},NodeInfo: func() interface{} {return manager.NodeInfo()},PeerInfo: func(id discover.NodeID) interface{} {if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {return p.Info()}return nil},})}

參考

源碼:https://github.com/ethereum/go-ethereum/tree/master/p2p

Kademlia算法:https://en.wikipedia.org/wiki/Kademlia

轉自:(魂祭心)?https://my.oschina.net/hunjixin/blog/1803029


如果你希望高效的學習以太坊DApp開發,可以訪問匯智網提供的最熱門在線互動教程:

1.適合區塊鏈新手的以太坊DApp實戰入門教程
2.區塊鏈+IPFS+Node.js+MongoDB+Express去中心化以太坊電商應用開發實戰

其他更多內容也可以訪問這個以太坊博客


總結

以上是生活随笔為你收集整理的以太坊 p2p Server 原理及实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

欧美一区二区日韩一区二区 | 日本三级久久久 | 久久久久久久久爱 | 亚洲国产精品久久 | 久久一区二 | 亚洲永久在线 | 中文在线a∨在线 | 97成人在线| 精品国产一区二区三区日日嗨 | 日韩av电影手机在线观看 | 久色婷婷 | 中文字幕免费高清在线观看 | 九草在线观看 | 丁香婷婷久久久综合精品国产 | 中文字幕一区二区三区在线播放 | 人人超碰免费 | 色在线免费 | 免费在线观看黄色网 | 99久久国产免费,99久久国产免费大片 | 99久久www| 在线观看激情av | 在线观看深夜视频 | 在线观看亚洲国产精品 | 亚洲精品九九 | 中文字幕在线观看一区 | 日本中文字幕高清 | 91精品国产99久久久久久久 | 一区二区理论片 | 又黄又刺激又爽的视频 | 91精品色 | 久久艹影院 | 国产精品原创在线 | 久久五月天综合 | 日日碰夜夜爽 | 欧美国产一区在线 | 日韩视频在线不卡 | 3d黄动漫免费看 | 夜夜躁日日躁狠狠久久88av | 天堂视频一区 | 久久久福利影院 | 91九色在线视频观看 | h动漫中文字幕 | 福利av在线| 欧美日韩精品区 | 99爱国产精品 | 欧美一级高清片 | 国产伦精品一区二区三区高清 | 中文字幕在线观看91 | 久久免费av电影 | 亚洲,国产成人av | 国产视频资源在线观看 | 在线成人高清电影 | 日韩免费电影网 | 欧美色综合久久 | 欧美va天堂va视频va在线 | www.久久久.com | 91精品国产自产在线观看 | 色哟哟国产精品 | 欧美久久精品 | 久久午夜色播影院免费高清 | 黄污视频网站大全 | 美女黄色网在线播放 | 免费看一级片 | 久久免费大片 | 欧美一级乱黄 | 激情婷婷色 | 日日操日日插 | 99精品久久99久久久久 | 亚洲国产精品成人女人久久 | a天堂免费 | 久久久三级视频 | 亚洲在线高清 | 国产精品不卡 | 黄色成人av在线 | 久久久久久久久久免费 | 久久综合操 | 91精品国产自产在线观看 | 久久大视频 | 天天干天天碰 | 超碰在线资源 | 久久视频6 | 日韩高清在线观看 | av超碰在线 | 欧美极品xxxx | 亚洲无毛专区 | 天天干天天做 | 日本护士三级少妇三级999 | 超碰在线免费福利 | 日韩大片免费观看 | 国内成人综合 | 国产精品热视频 | 国产又粗又长又硬免费视频 | 韩国一区二区av | 91福利小视频 | 国产在线视频一区 | 成人久久免费 | 丁香花五月 | 成人福利av| 五月婷激情 | 欧美日韩国产网站 | 国精产品一二三线999 | 久久国产精品久久国产精品 | 免费视频xnxx com| 久久精品国产精品亚洲 | 黄色av成人在线 | 91看片看淫黄大片 | 国产精品乱码高清在线看 | 亚洲综合色视频在线观看 | 久久不卡电影 | 特级西西444www大胆高清无视频 | 在线观看视频三级 | 久久久91精品国产一区二区三区 | 人人射网站 | 一区二区三区四区五区在线 | 国产在线不卡视频 | 黄色精品一区 | 精品一区二区三区久久久 | 免费日韩一区二区 | 亚洲欧洲国产视频 | 中国一级片免费看 | 中文av网站 | 中文字幕久久精品一区 | 免费在线观看成人小视频 | 免费看黄20分钟 | 91免费的视频在线播放 | 婷婷在线看 | 久久精品一区二区三 | 色欧美88888久久久久久影院 | 欧美污在线观看 | 亚洲日本一区二区在线 | 射射射综合网 | 日韩欧美在线免费观看 | 亚洲闷骚少妇在线观看网站 | 精品av网站 | 91豆花在线观看 | 精精国产xxxx视频在线播放 | 日韩精品在线一区 | 久久久久久久久久免费视频 | 久久人人爽人人爽人人片 | 国产亚洲人成网站在线观看 | 欧美少妇xx | 国产精品久久久久久久久免费 | 天天操天天透 | 国产又粗又长又硬免费视频 | 欧美不卡视频在线 | www.亚洲精品 | 日韩精品一区二区三区第95 | 91久久精品一区 | 91福利视频网站 | 成人国产网站 | 亚洲视频在线视频 | 69av视频在线 | 国产原创91 | 欧美一级电影片 | 亚洲涩涩网站 | 国内成人综合 | 91视频在线国产 | 亚洲综合视频网 | 久久99亚洲精品 | 久久免费看毛片 | 在线 你懂| 精品国产精品久久 | 欧美网址在线观看 | 在线观看免费日韩 | 日本少妇高清做爰视频 | 久久久www免费电影网 | 亚洲精品天天 | 丁香婷婷色 | 国产精品自产拍在线观看网站 | 97国产小视频 | 91精品999| 99综合影院在线 | 久久精品99国产国产精 | 亚洲.www| 欧美一二三区播放 | 久久精品国产精品亚洲 | 天天综合天天做天天综合 | 国产乱码精品一区二区蜜臀 | 久草9视频 | 国产h在线播放 | 探花视频在线版播放免费观看 | 男女拍拍免费视频 | a视频在线看 | 午夜精品久久久久久中宇69 | www色片| 一区二区三区日韩视频在线观看 | 2023亚洲精品国偷拍自产在线 | 日韩国产高清在线 | 在线成人看片 | 91日韩免费 | 国产一级一片免费播放放 | 911精品美国片911久久久 | 超碰人人草人人 | 国产精品久久久久久一二三四五 | 特级黄色片免费看 | 久久免费精品视频 | 国外av在线 | 国产免费观看高清完整版 | 欧美午夜一区二区福利视频 | 国产成人一二片 | 国产美女黄网站免费 | 欧美激情精品久久久久久 | 精品久久久久亚洲 | 日韩三区在线观看 | 免费日韩一区二区三区 | av成人免费在线看 | 国产视频资源在线观看 | 九九在线视频 | 久久亚洲精品国产亚洲老地址 | 五月婷婷综合在线 | 一区二区视频在线免费观看 | 久久综合加勒比 | 久久精品国产亚洲精品 | 亚洲精品高清视频在线观看 | 免费成人在线观看 | 美女免费视频黄 | 国产伦理精品一区二区 | 国产高清视频网 | 日韩欧美精品一区二区三区经典 | 在线 视频 一区二区 | 97国产视频 | 亚洲精品久久久久中文字幕二区 | 国产免费美女 | 一区二区三区 亚洲 | 国产精品自产拍在线观看蜜 | 国产黄色看片 | 日韩电影在线观看一区二区三区 | 国产精品尤物视频 | 国产精品99久久久精品 | 欧美精品一二 | 日韩黄色中文字幕 | 97视频免费在线看 | 99久久精品免费一区 | 中文在线www | 亚洲成人精品国产 | 久久伦理视频 | 99视频在线观看视频 | 夜夜操网站| 91精品国产91 | 精品免费一区二区三区 | 国产第页| 色网址99| 国产高清日韩欧美 | 久久99国产视频 | 国产成人精品一区二区三区网站观看 | 亚洲国产欧美一区二区三区丁香婷 | 91av大全 | 天天操天天摸天天爽 | 高清不卡免费视频 | 国产大尺度视频 | 麻豆传媒视频在线免费观看 | 久久99精品久久久久久 | 久久久婷 | 久久男女视频 | 中文字幕黄色av | 91人人澡 | 亚洲精品白浆高清久久久久久 | 曰韩在线 | 国产大尺度视频 | 日韩久久一区 | 在线亚洲免费视频 | 久久免费av | 日韩精品第一区 | 91在线色 | 天天插天天操天天干 | 在线观看欧美成人 | 六月丁香在线视频 | 国产午夜影院 | 中字幕视频在线永久在线观看免费 | 色噜噜在线观看 | 久草在线高清 | 人人爽人人爽 | 91成人精品一区在线播放69 | 日本最新中文字幕 | 人人干人人搞 | 国产精品视频全国免费观看 | 午夜 在线 | 9在线观看免费 | 天天综合久久综合 | 91福利影院在线观看 | 日日夜夜免费精品 | 特级aaa毛片| 天天干一干 | 亚洲丁香日韩 | 在线精品视频在线观看高清 | 欧美日韩精品区 | 美女视频黄在线 | 免费99精品国产自在在线 | 玖玖玖在线 | 69精品视频| 色综合网 | 久久人人爽人人人人片 | 久色网| 国产96在线| 中文在线√天堂 | 丁香视频全集免费观看 | www夜夜操com| 国产精品毛片一区二区在线看 | 久久在线视频精品 | 免费a级观看 | 欧美精品xxx | 97超碰站| 国产a视频免费观看 | 狠狠躁夜夜躁人人爽超碰91 | 国产一级特黄毛片在线毛片 | 国产精品视频内 | 国产精品系列在线观看 | 五月婷婷六月综合 | 欧美性生活大片 | 激情五月色播五月 | 亚洲国产一区在线观看 | 色视频 在线 | 久久午夜电影网 | av在线a | 欧美精品国产精品 | 欧美日韩中文字幕综合视频 | 99精品视频在线观看 | 在线草| 日韩久久久久久久 | 国产精品欧美久久久久天天影视 | 日本中文字幕影院 | 99在线热播精品免费99热 | 99久久精品国产一区二区成人 | 国内偷拍精品视频 | 97色婷婷人人爽人人 | 99精品视频在线免费观看 | av在线播放国产 | 一本一本久久a久久精品牛牛影视 | 国产日韩欧美精品在线观看 | 天天操,夜夜操 | 日韩成人免费观看 | 亚洲一区精品二人人爽久久 | 久久专区 | 久久精品一区二区国产 | 狠狠色丁香婷婷综合 | 91精品国产欧美一区二区 | 欧美一区二区三区在线观看 | 在线免费视频 你懂得 | 天天干天天拍天天操 | 国产精品久久久久久av | 亚洲撸撸 | 天天摸天天干天天操天天射 | 成年人视频在线免费观看 | 精品美女久久 | 黄色片视频在线观看 | 成人va天堂| www.午夜色.com| 草久视频在线观看 | 在线观看国产高清视频 | 久久精品国产免费观看 | 99国产视频在线 | 欧美成人h版在线观看 | 日一日操一操 | www日韩精品 | 亚洲精品美女视频 | 色婷婷亚洲 | 韩日av在线| 国产精品孕妇 | 日韩国产精品一区 | 精品国产一区二区三区免费 | 99视频精品全部免费 在线 | 久久精品一二三 | 国产成人高清在线 | 日韩精品久久久久久久电影竹菊 | 精品久久久久久亚洲 | 婷婷丁香九月 | 天天看天天干天天操 | 美女视频黄,久久 | 色小说av | 欧美一级爽 | 伊人丁香| 欧美日韩观看 | 四虎在线免费视频 | 国产美女免费看 | 日韩视频免费观看高清完整版在线 | 在线观av | 亚洲精品黄色在线观看 | 又大又硬又黄又爽视频在线观看 | 国产精品美女久久久免费 | 99在线视频网站 | 日韩精品一区二区三区外面 | 丁香狠狠| 在线看v片 | 中文亚洲欧美日韩 | 久久亚洲热 | 久久在线免费 | 婷婷激情综合网 | 欧美最新大片在线看 | 天天激情综合网 | 日日日天天天 | 人人添人人澡 | 天堂网av在线 | 久草热久草视频 | 色网av| 超碰日韩在线 | 黄色成人在线网站 | 久久久精品国产一区二区 | 成人av片免费观看app下载 | 一级黄色免费网站 | 69热国产视频 | 日日干天天 | 久热免费在线 | 久久99精品久久久久久三级 | 99热手机在线观看 | 久久综合综合久久综合 | 婷婷激情综合五月天 | 亚洲一级二级三级 | 久久人人艹 | 日韩一区二区三区免费电影 | 天天干婷婷 | 91亚洲精品视频 | 国产又粗又猛又黄又爽视频 | 伊人天堂久久 | 精品国产一区二区三区蜜臀 | 国产自在线观看 | 亚洲资源| 免费a v视频 | 精品一区 在线 | 免费看污在线观看 | 大胆欧美gogo免费视频一二区 | 亚洲黄a| 亚洲人成网站精品片在线观看 | 亚洲黄色在线观看 | 五月天丁香亚洲 | 免费av网站观看 | 操久在线| 中文视频在线播放 | 国产免费a | 一区二区三区精品久久久 | 午夜私人影院久久久久 | 日韩视频专区 | 综合伊人久久 | 国产伦精品一区二区三区照片91 | 免费av在线播放 | 日韩黄色在线电影 | 在线精品亚洲 | 欧美性做爰猛烈叫床潮 | 91网址在线看 | 国产精品青青 | 欧美在线观看视频一区二区 | 日韩精品视频一二三 | 特级西西人体444是什么意思 | 在线不卡的av | 四虎成人av| 国产精品av一区二区 | 亚洲精品网址在线观看 | 亚洲在线看 | 久操视频在线观看 | 福利二区视频 | 91av视频播放 | 婷婷久久综合九色综合 | 亚洲精品国产精品乱码不99热 | 不卡av在线 | 久久综合九色综合97_ 久久久 | 成人三级黄色 | 国产日本高清 | 国产h在线观看 | 国产精品网在线观看 | 高清有码中文字幕 | 精品一区中文字幕 | 亚洲最快最全在线视频 | 国产精品成久久久久三级 | 国产精品对白一区二区三区 | 99精品国产一区二区三区麻豆 | 久久精品中文视频 | 久久激情五月丁香伊人 | 欧美日韩观看 | 一区二区三区高清在线观看 | 亚洲免费小视频 | av成年人电影 | 日p在线观看 | av天天在线观看 | 看片的网址| 欧美在线观看视频 | 91麻豆精品国产91久久久更新时间 | 奇米777777| 中文字幕国产亚洲 | 精品一区二区日韩 | 狠狠操欧美 | 免费黄色在线网站 | 亚洲精品日韩一区二区电影 | 97天天综合网 | 亚洲免费精品一区二区 | 四虎国产免费 | 色噜噜在线观看视频 | 在线黄频| 美女精品久久久 | 精品999久久久 | 国内精品久久久久久久久久久 | 亚洲天堂在线观看完整版 | 亚洲一区二区高潮无套美女 | 欧美精品在线一区 | 久久综合狠狠综合久久综合88 | 91麻豆精品国产自产 | 成年人在线看片 | 99视频在线精品国自产拍免费观看 | 99爱这里只有精品 | 美女搞黄国产视频网站 | 久久国产视频网 | 欧美日韩一区二区三区在线免费观看 | 国产日韩在线看 | 欧美国产大片 | 9999亚洲| 国产一区二区在线免费播放 | 91久久精品一区二区三区 | 国产精品第三页 | 91桃色在线免费观看 | 99国产一区| 一区二区 不卡 | 中文字幕av影院 | 新版资源中文在线观看 | 日韩av中文字幕在线 | 色多多视频在线观看 | 开心色停停 | 天天干人人插 | 免费在线观看国产精品 | 日韩av一卡二卡三卡 | 亚洲乱码国产乱码精品天美传媒 | 国产精品久久久久毛片大屁完整版 | 精品综合久久久 | 亚州中文av | 午夜少妇一区二区三区 | 色婷婷综合久久久久中文字幕1 | 97精品超碰一区二区三区 | 婷婷深爱网 | 久久福利在线 | 精品国产免费人成在线观看 | 精品高清美女精品国产区 | 欧美日本在线观看视频 | 亚州精品一二三区 | 精品一区二区三区电影 | 国产精品美女久久久网av | 亚洲九九精品 | 丁香花在线观看免费完整版视频 | 91在线免费视频观看 | 2018好看的中文在线观看 | 亚洲精品视频观看 | 美女视频网站久久 | 91视视频在线直接观看在线看网页在线看 | 国产视频99 | 中文字幕色婷婷在线视频 | 狠狠色丁香婷婷综合欧美 | 精品黄色在线观看 | 国产欧美在线一区二区三区 | 波多野结衣一区二区 | 91免费看片黄 | 成人av动漫在线 | 亚洲精品乱码白浆高清久久久久久 | 久久久久久久久久影院 | 久久精品欧美视频 | 国产玖玖在线 | 久久国产日韩 | 黄色毛片网站在线观看 | 欧美日韩视频在线播放 | 国产专区一 | 色婷婷综合久久久 | 日韩三级视频在线观看 | 999电影免费在线观看2020 | 亚洲精品欧洲精品 | 精品久久久久久国产 | 国产一级视频在线免费观看 | 丁香婷婷基地 | 日本中文字幕网 | 精品国产三级 | 亚洲精品视频在线观看视频 | 国产一区免费在线观看 | 日韩视频欧美视频 | 午夜色婷婷 | 亚洲黄色免费 | 91福利社在线观看 | 日韩亚洲国产中文字幕 | 国产福利一区在线观看 | 色综合天天爱 | 亚洲免费婷婷 | 色婷婷综合久久久中文字幕 | 丝袜网站在线观看 | 97色婷婷| 亚洲激色| 午夜国产一区二区 | 黄色在线观看免费网站 | 99精品国产免费久久 | 国产精品美女www爽爽爽视频 | 色五月成人 | 国产成人在线一区 | 五月天激情综合 | 免费网站v | 成年人黄色大片在线 | 性色av免费看 | 六月色播| 韩日av在线 | 国产精品h在线观看 | 麻豆久久精品 | 婷婷六月激情 | 国产精品电影一区二区 | 精品国产成人在线影院 | 天天操夜夜操夜夜操 | 国产中文字幕网 | 国产美女精彩久久 | 91九色视频观看 | 五月天高清欧美mv | 成年人在线看视频 | 国产精品美女999 | 精品亚洲午夜久久久久91 | 精品久久久成人 | 色偷偷88888欧美精品久久久 | 最新国产福利 | 日韩欧美在线不卡 | 国产精品第 | 伊人狠狠色丁香婷婷综合 | 精品国产资源 | 欧美小视频在线观看 | 国产精品视频久久久 | 欧美久久久久久久久久久久 | 日韩网站在线看片你懂的 | 91av在线国产 | 日日操天天操夜夜操 | 天天综合人人 | 午夜av电影院 | 日韩专区视频 | 天天射色综合 | 欧美日本不卡 | 欧美一区日韩一区 | 国产精品成人一区二区三区吃奶 | 国产福利在线不卡 | 91x色 | 人人澡人人草 | 欧美一区二区三区免费观看 | 国产免费观看久久 | 中文字幕在线观看你懂的 | 午夜精品一区二区三区在线视频 | 最新真实国产在线视频 | 国产精品久久久久久久久久免费看 | 88av网站| 香蕉97视频观看在线观看 | 中文字幕首页 | 欧美少妇的秘密 | 日韩精品欧美视频 | 国产在线a免费观看 | 国产 视频 高清 免费 | 中文字幕人成乱码在线观看 | h视频在线看 | 天天噜天天色 | 狠狠色丁香久久婷婷综 | 在线观看免费高清视频大全追剧 | 在线观看视频你懂的 | 99久久99久久精品国产片果冰 | 亚洲午夜精品在线观看 | 亚洲欧美国产精品 | 久久精品电影 | 麻豆视频免费入口 | 婷婷新五月 | 国产精品久久久久久久久久直播 | 亚洲视频2 | 狠狠干电影 | 午夜精品一区二区三区在线视频 | 成年人免费电影在线观看 | 808电影 | 99精品欧美一区二区三区黑人哦 | 欧洲在线免费视频 | 久草综合视频 | 久久久久国产精品午夜一区 | 国产日韩欧美在线看 | 国产成人精品女人久久久 | 国产成人三级在线观看 | 黄色一二级片 | 日韩欧美69 | 黄色av电影 | 丁香婷婷综合网 | 亚洲精品乱码白浆高清久久久久久 | 91欧美国产 | 成人在线免费视频 | 国产一区福利在线 | 日韩高清三区 | 日韩网站在线播放 | 国产精品久久久久影视 | 一区二区三区免费在线播放 | 久久av一区二区三区亚洲 | 久久电影网站中文字幕 | 日韩精品播放 | 欧美精品一二 | 日韩动漫免费观看高清完整版在线观看 | 天天在线视频色 | 一区二区三区中文字幕在线 | 精品一区二区免费在线观看 | 国内一区二区视频 | 国产精品美乳一区二区免费 | 国产一区二区精 | 欧美成人一区二区 | 麻豆国产精品视频 | 一区二区三区日韩精品 | 99精品国自产在线 | 久久亚洲免费 | 97精品国产97久久久久久 | 国产一级片播放 | 五月激情天| 欧美一级免费黄色片 | 欧美成人精品三级在线观看播放 | 国产午夜小视频 | 中文字幕av有码 | 久久久久国产精品免费 | 亚洲精品 在线视频 | 成人av片在线观看 | 久久婷婷五月综合色丁香 | 欧美日韩在线观看一区 | 碰碰影院 | 久久成人综合视频 | 91精品国产综合久久福利 | 一级黄色片在线观看 | 六月色丁 | 992tv又爽又黄的免费视频 | 欧美男同网站 | 在线观看黄色av | 日韩av在线看 | 中文字幕区 | 日韩激情综合 | 亚洲免费不卡 | 视频在线国产 | 国内精品久久久精品电影院 | 中文字幕av最新更新 | 久久综合久久八八 | 激情五月婷婷丁香 | 久久成人欧美 | 国内综合精品午夜久久资源 | 日韩欧美视频一区二区 | av解说在线观看 | 中文字幕亚洲综合久久五月天色无吗'' | 97超视频免费观看 | 久久有精品 | 国产真实精品久久二三区 | 麻豆系列在线观看 | 黄色大片网 | 国产96在线视频 | 91mv.cool在线观看 | 国产精品免费人成网站 | 黄色不卡av | 久久激情五月婷婷 | 午夜视频在线观看一区二区三区 | 国产精品久久久久影院 | 一区二区三区四区五区六区 | 超碰在线观看99 | 中文字幕在线观看一区二区 | 在线观看视频一区二区三区 | 二区三区毛片 | 久久免费播放 | 丝袜+亚洲+另类+欧美+变态 | 国产美女在线精品免费观看 | 九九热精 | 九九涩涩av台湾日本热热 | 青草视频在线 | 在线播放亚洲激情 | 狠狠色丁香婷婷综合久小说久 | 97色国产 | 天天天天色综合 | 亚洲成免费 | 国产精品嫩草55av | 日韩在线免费播放 | 久草在线视频资源 | 一区二区三区免费在线观看视频 | 欧美一区二区三区不卡 | 成人动漫视频在线 | 啪啪免费观看网站 | 中文字幕在线观看的网站 | 国产一区二区在线免费 | 999精品网| 四虎国产视频 | 欧美伊人网 | 黄色aaa级片 | 国产精品久久久av久久久 | 欧美一级电影在线观看 | 久操免费视频 | 国产黄a三级 | 中文字幕高清av | 国产区精品在线观看 | 91在线免费观看国产 | 色网影音先锋 | 亚洲一二区精品 | av蜜桃在线 | 国产亚洲久一区二区 | 国产中文欧美日韩在线 | 九九九九精品 | 国产视频一区二区在线播放 | 国产精品99久久久久久宅男 | 毛片美女网站 | 91一区一区三区 | 一区二区三区高清在线 | 色午夜| 中文字幕久久精品 | 91麻豆文化传媒在线观看 | 精品在线观看一区二区 | 国产精品久久电影网 | 99热这里只有精品在线观看 | 中文字幕观看视频 | 久久综合中文色婷婷 | 久久中文网 | 免费观看www视频 | 国产精品久久久久婷婷二区次 | 欧美日韩成人一区 | 国产在线精品一区二区三区 | 一级一级一片免费 | 在线免费观看亚洲视频 | 狠狠婷婷 | 字幕网av | 久久精品—区二区三区 | 久久精品99国产精品 | 日韩精品一区二区免费 | 天天干夜夜干 | 国产高清av免费在线观看 | 欧美色黄| 久久社区视频 | 国产精品刺激对白麻豆99 | 天天操天天干天天操天天干 | 天天色综合三 | 国产视频综合在线 | 黄色一级在线观看 | 日韩成人精品一区二区 | 在线 国产 日韩 | 天天操狠狠操夜夜操 | 久久综合狠狠综合久久激情 | 五月天丁香 | 国产精品免费视频观看 | 日韩午夜电影院 | 99产精品成人啪免费网站 | 视频99爱 | 久久久久久久久久久影视 | 日韩丝袜在线观看 | 亚洲综合色视频 | 一级黄色电影网站 | 中文资源在线播放 | 一区二区三区四区五区六区 | 日韩r级在线 | 天天干天天玩天天操 | 久草香蕉在线 | 99热精品国产 | 免费无遮挡动漫网站 | 成年美女黄网站色大片免费看 | 亚洲欧洲精品视频 | 亚洲年轻女教师毛茸茸 | 久久成人精品电影 | 国产99久久久国产精品免费二区 | 免费男女羞羞的视频网站中文字幕 | 婷婷激情久久 | 91.麻豆视频 | 国产亚洲精品久久久久久久久久 | 韩日三级av | 九色最新网址 | 青春草免费在线视频 | 久久无码精品一区二区三区 | 福利区在线观看 | 中文字幕免费观看视频 | 在线观看视频三级 | 五月婷婷综合激情网 | 欧美综合在线观看 | 国产视频69| 99性视频 | 日韩小视频 | 久久久久电影网站 | 一级片视频在线 | 丁香六月婷| 欧美日韩高清一区 | 中文字幕你懂的 | 久久色视频 | 91在线永久 | 欧美日韩国产色综合一二三四 | 最新av网址大全 | 亚洲爱爱视频 | 在线看片中文字幕 | 国产精品成人品 | 国内精品久久天天躁人人爽 | 午夜日b视频 | 夜夜操综合网 | 天天操天天射天天爱 | 91亚洲精品久久久蜜桃网站 | 国产日韩欧美综合在线 | 在线观看免费版高清版 | 黄色小说视频网站 | 国产精品视频专区 | 免费a级黄色毛片 | 中文字幕在线观看网址 | 日韩精品视频在线免费观看 | 天天草av | 久久久免费精品 | 中文字幕免费一区 | av电影在线免费 | 九草在线视频 | 天天干天天在线 | 日韩免费播放 | 亚洲一区二区三区四区在线视频 | 国产一级电影网 | 色欧美成人精品a∨在线观看 | 成人精品视频久久久久 | 欧美日韩在线视频免费 | 天天操婷婷 | 网址你懂的在线观看 | 中文字幕在线影视资源 | 国产成人精品一区二区三区网站观看 | 中文字幕在线免费看线人 | 国产精品九九热 | 成人av日韩| 日本黄色a级大片 | 激情久久久久久久久久久久久久久久 | 国产黄色免费看 | 中文av网站| 黄色特级一级片 | 黄色一级在线观看 | 7777xxxx| 国产97在线看 | 日韩免费视频观看 | 日韩免费视频在线观看 | 欧美日韩电影在线播放 | 天天操夜夜做 | 久久免费高清 | 亚洲视频精品 | 黄色一级性片 | 久久精品国亚洲 | 韩国av免费在线观看 | 久久久久久久久久久高潮一区二区 | 免费能看的av | 国产 日韩 欧美 自拍 | 亚洲黄色激情小说 | 婷婷激情5月天 | 久久免费高清视频 | 免费一级片视频 | 久久99热精品这里久久精品 | 三级动图| 狠狠躁夜夜躁人人爽超碰91 | 日韩国产精品久久久久久亚洲 | 久久久 激情| 九色视频网址 | 一区二区免费不卡在线 | 国产aa精品| 在线 精品 国产 | 亚洲最新av | 91成人破解版| 国产在线不卡一区 | 一区二区精品视频 | 久久久精品高清 | 91视频免费看网站 | 国产午夜精品免费一区二区三区视频 | 永久精品视频 | 亚洲精品成人av在线 | 亚洲女人天堂成人av在线 | 国产精品不卡在线 | 国产精品一区免费在线观看 | 国产一及片 | 亚洲精品美女久久 | 国产午夜精品久久久久久久久久 | 久久国产精品区 | 国产精品在线看 | 午夜色影院| 欧美一二三四在线 | 99在线观看 | 久久人人97超碰com | 91天天视频 | 国产男女无遮挡猛进猛出在线观看 | 福利视频一区二区 | 国产视频 亚洲视频 | 99热在线国产 | 亚洲黄色免费 | 中文字幕色婷婷在线视频 | 国产精品一区二区在线免费观看 | 日韩动漫免费观看高清完整版在线观看 | 99精品影视 | 丁香九月婷婷综合 | 在线观看视频你懂的 | 久久精品视频一 | 免费在线精品视频 | 久免费视频 | 国产老妇av | 摸阴视频 | 黄色小说视频网站 | 91亚洲精品久久久蜜桃网站 | 这里只有精彩视频 | 一二三区av | 激情综合网色播五月 | 天堂素人在线 | 成人一级片免费看 | 丁香婷婷激情网 | 国产精品久久一卡二卡 | 亚洲综合欧美日韩狠狠色 | www.大网伊人 | 99热最新| 午夜视频免费播放 | 丁香电影小说免费视频观看 | 字幕网av | 国产一区视频在线观看免费 | 人人网人人爽 | 欧洲精品在线视频 | 成人全视频免费观看在线看 | 区一区二区三在线观看 | 精品国产大片 | 国产亚洲在线观看 | 亚洲欧洲国产日韩精品 | 欧美日本不卡视频 | 欧美精品一区在线发布 | 免费亚洲黄色 | 免费日韩一区二区三区 | 国产精品欧美久久久久天天影视 | 国产精品免费观看视频 |