设为首页 加入收藏

TOP

go语言打造p2p网络(一)
2019-05-23 14:34:42 】 浏览:153
Tags:语言 打造 p2p 网络


传送门: 柏链项目学院



就像1000个人眼中有1000个哈姆雷特一样,每个人眼中的区块链也是不一样的!作为技术人员眼中的区块链就是将各种技术的融合,包括密码学,p2p网络,分布式共识机制以及博弈论等。我们今天就来讨论一下区块链技术中的p2p网络,这是一种点到点的通信技术。

说到p2p通信,它并没有名字看上去那样简单,在网络世界里实现p2p还是需要一些手段的!很多朋友可能会说,实现一个c/s模式的点到点通信很简单呀,但是前提是彼此可以看见,比如服务器在公网,或者服务器和客户端都在同一个局域网内,我们要探讨的p2p通信是指通信的双方分别在两个局域网内部!

由于在两个局域网内部,两台设备并没有公网IP,彼此要通信需要借助路由器,但是路由器又会对不识别的ip进行过滤,也就是路由器有个陌生人排除机制!怎么办呢?类似于我们去一个安保较为严格的场所时,需要内部的工作人员接引才可入内,在网络编程中也是这样的原理!但和现实中不同的是,假设设备A想和另一个局域网的设备B通信,设备B是并不认识设备A的,设备A通过路由器NAT(Network Address Translation,网络地址转换)技术获得了一个公网映射IP,但是设备B并不认识,那么怎么样能让两者通信呢?所以这个时候需要一个介绍人,此时需要有一个公网的服务器作为媒介,介绍两个人介绍,当B设备对应路由器添加了A设备对应的公网IP后,A设备就可以与B设备建立连接了,这个时候就可以顺畅的通信了!

Server S
                    10.47.58.139:9527
                           |
                           |
    +----------------------|----------------------+
    |                                             |
  NAT A                                         NAT B
122.27.219.161:10001                            123.29.210.131:10002
    |                                             |
    |                                             |
 Client A                                      Client B
 192.168.1.126:9901                           192.168.1.102:9902

如上图所示,CLientA与ClientB想要通信,因为两个客户端都在各自的局域网内,都是通过NAT技术生成公网映射IP的,想要彼此访问必须通过一个中间服务器进行中介介绍,这样两个客户端才能彼此认识并建立连接,否则双方直接通信都会被路由器丢弃。那么为什么非要用NAT呢?直接为每个Client分配一个公网IP不可以吗?这是由于IPv4的限制,公网IP数量是有限的,我们国家拿到的公网IP段更是有限,甚至不及美国一所大学的IP段数量多。这样也就不可能为每个机器都分配一个公网IP,正因为此NAT技术才非常重要,它可以很好的帮我们解决公网IP不足的问题。

接下来我们还是介绍一下NAT的原理和类型:

NAT主要可以分为两类:

  • 基本NAT,这种要求NAT有多个公网IP,这样可以将公网IP和内网设备静态绑定
  • NAPT(Network Address Port Translation),更为常见的NAT,内网设备的网络请求通过不同端口加以映射

针对NAPT端口的映射方式,又可以分为四种形式:

  • 完全圆锥型NAT( Full Cone NAT ),将从一个内部IP地址和端口来的所有请求,都映射到相同的外部IP地址和端口。并且,任何外部主机通过向映射的外部地址发送报文,都可以实现和内部主机进行通信。
  • 地址限制圆锥型NAT( Address Restricted Cone NAT ),将从相同的内部IP地址和端口来的所有请求映射到相同的公网IP地址和端口。但是与完全圆锥型NAT不同,当且仅当内部主机之前已经向公网主机发送过报文,此时公网主机才能向内网主机发送报文。
  • 端口限制圆锥型NAT( Port Restricted Cone NAT ),端口受限圆锥型NAT增加了端口号的限制,当前仅当内网主机之前已经向公网主机发送了报文,公网主机才能和此内网主机通信。
  • 对称型NAT( Symmetric NAT),把从同一内网地址和端口到相同目的地址和端口的所有请求,都映射到同一个公网地址和端口。如果同一个内网主机,用相同的内网地址和端口向另外一个目的地址发送报文,则会用不同的映射。

本人经过检测发现,本机的NAT类型为上述第四种:Symmetric NAT

知识点普及后,我们继续来实践我们之前所说的p2p技术,也就是两个设备之间的通信问题,由于两个设备分别在各自的网络内部,我们也称这种行为为打洞!

接下来我们用go语言来实现这个打洞技术,主要使用UDP来实现,具体流程如下:

  1. 建立UDP服务器 S
  2. 建立A、B客户端,分别与S建立会话,SA与SB
  3. S当好中介人,将A的ip+端口通过SB告诉B,将B的ip+端口通过SA告诉A
  4. A向B公网地址发送一个UDP包,代表握手,打通A-B的路径
  5. B向A公网地址发送一个UDP包,A-B的会话建立成功

代码如下:

//server.go
    "log"
    "net"
    "time"
)

func main() {
    listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 9527})
    if err != nil {
        fmt.Println(err)
        return
    }
    log.Printf("本地地址: <%s> \n", listener.LocalAddr().String())
    peers := make([]net.UDPAddr, 0, 2)
    data := make([]byte, 1024)
    for {
        n, remoteAddr, err := listener.ReadFromUDP(data)
        if err != nil {
            fmt.Printf("error during read: %s", err)
        }
        log.Printf("<%s> %s\n", remoteAddr.String(), data[:n])
        peers = append(peers, *remoteAddr)
        if len(peers) == 2 {
            log.Printf("进行UDP打洞,建立 %s <--> %s 的连接\n", peers[0].String(), peers[1].String())
            listener.WriteToUDP([]byte(peers[1].String()), &peers[0])
            listener.WriteToUDP([]byte(peers[0].String()), &peers[1])
            time.Sleep(time.Second * 8)
            log.Println("中转服务器退出,仍不影响peers间通信")
            return
        }
    }
}

服务端显示如下:

ykdeMac-mini:study yekai$ ./server 
2019/04/03 14:50:13 本地地址: <[::]:9527> 
2019/04/03 14:51:48 <192.168.1.102:9901> hello, I'm new peer:yekai1
2019/04/03 14:52:5
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇go语言框架gin之集成swagger 下一篇go语言调度器源代码情景分析之一..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目