Golang p2p打洞 ipv6

214次阅读
没有评论

共计 2765 个字符,预计需要花费 7 分钟才能阅读完成。

go p2p udp 打洞 实现数据不经过服务器 所述,ipv4 p2p 打洞,ipv6只需做简单修改

ps:服务端和客户端需支持ipv6,测试前建议ping通

服务端

package main

import (
  "fmt"
  "log"
  "net"
  "time"
)

func main() {
  listener, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 9982})
  if err != nil {
    fmt.Println(err)
    return
  }
  log.Printf("lcoal addr : <%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 hole,establish %s <--> %s Connection\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("transit server exit and peers can still communicate.")
      //清空切片等待下一次连接
      peers = peers[0:0]
      return
    }
  }
}

 

客户端:

package main

import (
  "fmt"
  "log"
  "math/rand"
  "net"
  "strconv"
  "strings"
  "time"
)

var tag string

const HAND_SHAKE_MSG = "我是打洞消息"

func main() {
  // 当前进程标记字符串,便于显示
  tag = strconv.Itoa(rand.Int())
  srcAddr, _ := net.ResolveUDPAddr("udp", "")
  // srcAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9983} // 注意端口必须固定
  listener, err := net.ListenUDP("udp", srcAddr)
  fmt.Println("listen on:", listener.LocalAddr())
  dstAddr := &net.UDPAddr{IP: net.ParseIP("【服务端IPV6地址】"), Port: 9982}
  // conn, err := net.DialUDP("udp", srcAddr, dstAddr)

  if err != nil {
    fmt.Println(err)
  }
  if _, err = listener.WriteToUDP([]byte("hello, I'm new peer:"+tag), dstAddr); err != nil {
    log.Panic(err)
  }
  data := make([]byte, 1024)
  n, remoteAddr, err := listener.ReadFromUDP(data)
  if err != nil {
    fmt.Printf("error during read: %s", err)
  }
  anotherPeer := parseAddr(string(data[:n]))
  fmt.Printf("local:%s server:%s another:%s\n", srcAddr, remoteAddr, anotherPeer.String())

  // 开始打洞
  bidirectionHole(srcAddr, &anotherPeer, listener)

}

func parseAddr(addr string) net.UDPAddr {
  t := strings.Split(addr, "[")
  t = strings.Split(t[1], "]:")
  fmt.Println("对端IPV6地址:", t[0])
  fmt.Println("对端端口:", t[1])
  port, _ := strconv.Atoi(t[1])
  return net.UDPAddr{
    IP:   net.ParseIP(t[0]),
    Port: port,
  }
}

func bidirectionHole(srcAddr *net.UDPAddr, anotherAddr *net.UDPAddr, conn *net.UDPConn) {
  defer conn.Close()
  // 向另一个peer发送一条udp消息(对方peer的nat设备会丢弃该消息,非法来源),用意是在自身的nat设备打开一条可进入的通道,这样对方peer就可以发过来udp消息
  for i := 0; i < 5; i++ {
    if _, err := conn.WriteToUDP([]byte(HAND_SHAKE_MSG), anotherAddr); err != nil {
      log.Println("send handshake:", err)
    }
    data := make([]byte, 1024)
    n, anotherAddr_t, err := conn.ReadFromUDP(data)
    if n > 0 {
      anotherAddr = anotherAddr_t
      fmt.Println("ip地址变更")
      break
    } else {
      fmt.Println("read from server err :", err)
    }
    time.Sleep(time.Second * 1)
  }
  go func() {
    for {
      time.Sleep(10 * time.Second)
      if _, err := conn.WriteToUDP([]byte("from ["+tag+"]"), anotherAddr); err != nil {
        log.Println("send msg fail", err)
      }
    }
  }()
  for {
    data := make([]byte, 1024)
    n, _, err := conn.ReadFromUDP(data)
    if err != nil {
      log.Printf("error during read: %s\n", err)
    } else {
      log.Printf("收到数据:%s\n", data[:n])
    }
  }
}

 

 

正文完
 
admin
版权声明:本站原创文章,由 admin 2022-04-19发表,共计2765字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码