前言: 穷人指没钱或者不肯花太多钱(我既属于前者也属于后者TT),分布式网络主要指主机网络环境分布在不一样的地理环境好比不一样省或者不一样国家(谁尚未一个比较便宜的国外vps不是\~\~)html
既然没有办法改变世界就改变本身吧.git
注意:我是指尽量的改善当前的网络环境,你固然能够说出一堆的极端环境,不过那真不是我想要解决的.github
第一种解决方案能够解决主机之间的连通性,可是并无试图改善网络环境不佳的局面,即,基于当前网络环境.再者各类***的安装部署都太负载且装一堆有的没的.golang
其实我不太肯定是否是全部ipsec或者其余***方式会不会经过一些机制解决网络环境不佳的状况,若有错误,还望指正.算法
第二种方案中的kcp介绍以下编程
KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟下降 30%-40%,且最大延迟下降三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,须要使用者本身定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都须要外部传递进来,内部不会有任何一次系统调用。json
而kcptun则是在kcp协议的基础上,创建隧道,但只是映射某个端口,实在是让人难过,一个一个的端口映射很难维护.网络
其余方案指代理或者其余***socket
我暂时没有在找到比较好的解决方案,因此我打算本身撸一个,可是当下只是写了一个原型(可能没时间写),有兴趣且码代码速度快的参考一下个人设计开发一下,开发好了能够给我用用^_^tcp
golang
google出品必属精品.
kcp
用于这里想解决的问题是网络环境不佳的问题,因此纯udp或者tcp都是不太适合的.
原本个人愿景要再大一些的,即不仅是在有公网IP的境况下打通网络,且能穿透nat(指必定概率,有一些nat环境的确打不穿.)
因为大部分时间是在研究怎么经过kcp在绑定特定端口的状况下既能充当client发起请求又能做为server接受请求,可是kcp虽然是基于udp,可是把本身包装成了类tcp,因此监听的境况下就不能使用相似WriteToUDP的方法发送了(当下的确没看到相似接口),因此若是使用kcp的方式穿透nat,那么心跳的流程以及存活状态的逻辑就变得比较复杂了,上面的设计方案主要指都拥有公网IP或者两端至少一段拥有公网IP的状况.
因此原型中并无设计kcp的代码.
hubserver
// fast***_hub.go package main import ( "log" "net" "os" "syscall" "time" // reuse "github.com/kavu/go_reuseport" kcp "github.com/xtaci/kcp-go" ) func main() { var err error var conn *net.UDPConn if len(os.Args) < 2 { os.Exit(-1) } raddr := os.Args[1] localAddr, _ := net.ResolveUDPAddr("udp", ":8887") serverAddr, _ := net.ResolveUDPAddr("udp", raddr) conn, err = net.DialUDP("udp", laddr, raddr) conn.W if err != nil { log.Println("dial udp err") log.Println(err) } _, err = conn.Write([]byte("hello")) if err != nil { log.Println("udp write error") log.Println(err) } buf := make([]byte, 1024) n, _, err := conn.ReadFromUDP(buf) log.Println("udp read data: ", string(buf[:n])) conn.Close() heartbeat(localAddr, serverAddr) klisten, err := kcp.Listen(":8887") if err != nil { log.Println("listen error") log.Println(err) } log.Println("accept start") for { conn, err := klisten.Accept() if err != nil { log.Println("accpet error") log.Println(err) continue } go func() { for { buf := make([]byte, 1024) n, err := conn.Read(buf) if err != nil { log.Println("read error.") log.Println(err) } log.Println("recieve data: ", string(buf[:n])) _, err = conn.Write([]byte("hello")) if err != nil { log.Println("kcp write error") log.Println(err) } log.Println("remote addr: ", conn.RemoteAddr()) time.Sleep(3 * time.Second) } }() } }
peerclient.go
package main import ( "encoding/json" "flag" "fmt" "log" "net" "os" "os/exec" "strings" // "fast***/common" "github.com/songgao/water" "golang.org/x/net/ipv4" ) const ( BUFFERSIZE = 1500 MTU = "1300" ) var ( hubServer = flag.String("hub", "", "server addr like 192.168.11.100:8796") local = flag.String("local", "", "local ip like 172.16.97.101") listen = flag.String("listen", ":6222", "udp for bind") port = flag.String("port", "9999", "local port like 9999, default 9999, if you want to run multi clinet in same machine,change the port") peerMap = make(map[string]string) ) func checkFatalErr(err error, msg string) { if err != nil { log.Println(msg) log.Fatal(err) } } func runIP(args ...string) { cmd := exec.Command("/sbin/ip", args...) cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout err := cmd.Run() if err != nil { log.Fatal("Error runing /sbin/ip:", err) } } func main() { flag.Parse() // parse args if *hubServer == "" { flag.Usage() log.Fatal("\nhub Server is not specified") } if *local == "" { flag.Usage() log.Fatal("\nlocal ip is not specified") } if *port == "" { *port = fmt.Sprintf(":%s", *port) } // 将地址字符串解析成*net.UDPAddr hubAddr, err := net.ResolveUDPAddr("udp", *hubServer) checkFatalErr(err, "Unable to resolve server UDP socket") listenAddr, err := net.ResolveUDPAddr("udp", *listen) checkFatalErr(err, "Unable to resolve local UDP socket") // 初始化建立的tun设备的配置文件 config := water.Config{ DeviceType: water.TUN, } // 建立一个tun设备 iface, err := water.New(config) checkFatalErr(err, "Unable to allocate TUN interface: ") log.Println("Interface allocated: ", iface.Name()) // 设置ip地址并启动设备 runIP("link", "set", "dev", iface.Name(), "mtu", MTU) runIP("addr", "add", *local, "dev", iface.Name()) runIP("link", "set", "dev", iface.Name(), "up") // 监听一个udp socket,经过listenUDP建立的socket,既能发送到指定的ip地址也能接受 conn, err := net.ListenUDP("udp", listenAddr) checkFatalErr(err, "Unable to connect server") defer conn.Close() privateIP := strings.Split(*local, "/") // 将本身的内网IP发送给hubserver conn.WriteToUDP([]byte(privateIP[0]), hubAddr) go func() { buf := make([]byte, BUFFERSIZE) // 不停的接受信息 for { n, addr, err := conn.ReadFromUDP(buf) if addr.String() == hubAddr.String() { log.Println("recieve data from server:") // 解析hubserver发送过来的peermap err = json.Unmarshal(buf[:n], &peerMap) if err != nil { log.Println("peermap unmarshal error") log.Println(err) } } else { log.Println("recive data from peer:") } if err != nil || n == 0 { fmt.Println("Error: ", err) continue } log.Println(string(buf[:n])) // 将对端发送过来的数据写到本地的tun设备 iface.Write(buf[:n]) } }() packet := make([]byte, BUFFERSIZE) // 不停的读取本地tun设备接受到的数据包 for { plen, err := iface.Read(packet) if err != nil { break } // 解析数据包头部 header, _ := ipv4.ParseHeader(packet[:plen]) dstIP := header.Dst.String() // 若是数据发送的目标地址在peermap内则经过udp发送到对端 realDest, ok := peerMap[dstIP] if ok { realDestAddr, err := net.ResolveUDPAddr("udp", realDest) if err != nil { log.Println("resolve real dest ip error") log.Println(err) continue } fmt.Printf("Sending to remote: %+v (%=v)\n", header, err) conn.WriteTo(packet[:plen], realDestAddr) } else { continue } } }
依次启动hubserver,peer1,peer2, 效果以下
试试网络是不是通的.
这个原型没有验证,也没有kcp,也没有数据加密...仅是最基础的功能被实现了,数据传递.
源代码:https://github.com/youerning/blog/tree/master/fast***
主要灵感来自这篇文章:https://nsl.cz/using-tun-tap-in-go-or-how-to-write-***/
可是我作了一下修改,将数据包转发到指定的对端.加了个hubserver.
参考链接:
https://github.com/skywind3000/kcp