nps/client/client.go
2019-04-08 17:01:08 +08:00

182 lines
4.3 KiB
Go
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package client
import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net"
"time"
)
type TRPClient struct {
svrAddr string
bridgeConnType string
proxyUrl string
vKey string
tunnel *mux.Mux
signal *conn.Conn
ticker *time.Ticker
cnf *config.Config
}
//new client
func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string, cnf *config.Config) *TRPClient {
return &TRPClient{
svrAddr: svraddr,
vKey: vKey,
bridgeConnType: bridgeConnType,
proxyUrl: proxyUrl,
cnf: cnf,
}
}
//start
func (s *TRPClient) Start() {
retry:
c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
if err != nil {
logs.Error("The connection server failed and will be reconnected in five seconds")
time.Sleep(time.Second * 5)
goto retry
}
logs.Info("Successful connection with server %s", s.svrAddr)
//monitor the connection
go s.ping()
s.signal = c
//start a channel connection
go s.newChan()
//start health check if the it's open
if s.cnf != nil && len(s.cnf.Healths) > 0 {
go heathCheck(s.cnf.Healths, s.signal)
}
//msg connection, eg udp
s.handleMain()
}
//handle main connection
func (s *TRPClient) handleMain() {
for {
flags, err := s.signal.ReadFlag()
if err != nil {
logs.Error("Accept server data error %s, end this service", err.Error())
break
}
switch flags {
case common.NEW_UDP_CONN:
//read server udp addr and password
if lAddr, err := s.signal.GetShortLenContent(); err != nil {
logs.Warn(err)
return
} else if pwd, err := s.signal.GetShortLenContent(); err == nil {
go s.newUdpConn(string(lAddr), string(pwd))
}
}
}
s.Close()
}
func (s *TRPClient) newUdpConn(rAddr string, md5Password string) {
var localConn net.PacketConn
var err error
var remoteAddress string
if remoteAddress, localConn, err = handleP2PUdp(rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil {
logs.Error(err)
return
}
l, err := kcp.ServeConn(nil, 150, 3, localConn)
if err != nil {
logs.Error(err)
return
}
logs.Trace("start local p2p udp listen, local address", localConn.LocalAddr().String())
//接收新的监听得到conn
for {
udpTunnel, err := l.AcceptKCP()
if err != nil {
logs.Error(err)
l.Close()
return
}
if udpTunnel.RemoteAddr().String() == string(remoteAddress) {
conn.SetUdpSession(udpTunnel)
logs.Trace("successful connection with client ,address %s", udpTunnel.RemoteAddr().String())
//read link info from remote
l := mux.NewMux(udpTunnel, s.bridgeConnType)
for {
connMux, err := l.Accept()
if err != nil {
continue
}
go s.handleChan(connMux)
}
}
}
}
//mux tunnel
func (s *TRPClient) newChan() {
tunnel, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl)
if err != nil {
logs.Error("connect to ", s.svrAddr, "error:", err)
return
}
s.tunnel = mux.NewMux(tunnel.Conn, s.bridgeConnType)
for {
src, err := s.tunnel.Accept()
if err != nil {
logs.Warn(err)
s.Close()
break
}
go s.handleChan(src)
}
}
func (s *TRPClient) handleChan(src net.Conn) {
lk, err := conn.NewConn(src).GetLinkInfo()
if err != nil {
src.Close()
logs.Error("get connection info from server error ", err)
return
}
//host for target processing
lk.Host = common.FormatAddress(lk.Host)
//connect to target
if targetConn, err := net.Dial(lk.ConnType, lk.Host); err != nil {
logs.Warn("connect to %s error %s", lk.Host, err.Error())
src.Close()
} else {
logs.Trace("new %s connection with the goal of %s, remote address:%s", lk.ConnType, lk.Host, lk.RemoteAddr)
conn.CopyWaitGroup(src, targetConn, lk.Crypt, lk.Compress, nil, nil, false, nil)
}
}
func (s *TRPClient) ping() {
s.ticker = time.NewTicker(time.Second * 5)
loop:
for {
select {
case <-s.ticker.C:
if s.tunnel != nil && s.tunnel.IsClose {
s.Close()
break loop
}
}
}
}
func (s *TRPClient) Close() {
if s.tunnel != nil {
s.tunnel.Close()
}
if s.signal != nil {
s.signal.Close()
}
if s.ticker != nil {
s.ticker.Stop()
}
}