Exemple #1
0
func main() {
	connChan := make(chan netchan.ConnRes)
	exitChan := make(chan int)
	defer close(exitChan)

	dial, err := proxyclient.NewProxyClient("direct://0.0.0.0:0000/")
	if err != nil {
		panic(err)
	}

	go func() {
		netchan.ChanDialTimeout(dial, 0, connChan, exitChan, true, nil, nil, "tcp", "www.163.com:80", 5*time.Second)
		close(connChan)
	}()

	for c := range connChan {
		fmt.Printf("目标 %v 耗时 %v 。\r\n", c.Conn.RemoteAddr(), c.Ping)
	}

}
func (su *tcppingUpStream) DialTimeout(network, address string, timeout time.Duration) (net.Conn, UpStreamErrorReporting, error) {
	// 尝试使用缓存中的连接
	item, err := su.connCache.GetOptimal(address)
	if err == nil {

		ctimeout := item.TcpPing
		if ctimeout < 100*time.Millisecond {
			ctimeout += 20 * time.Millisecond
		} else if ctimeout < 500*time.Millisecond {
			ctimeout += 50 * time.Millisecond
		} else {
			ctimeout += 100 * time.Millisecond
		}

		// 考虑了下,还是使用原始的连接方式,而没有使用 dialChan
		n := time.Now()
		c, err := item.dialClient.pc.DialTimeout(network, item.IpAddr, ctimeout)
		if err == nil {
			fmt.Printf("缓存命中:%v 代理:%v IP:%v \r\n", address, item.dialName, item.IpAddr)
			go su.connCache.Updata(address, item.IpAddr, time.Now().Sub(n)+item.dialClient.correctDelay, item.dialClient, item.dialName)
			ErrorReporting := &UpStreamErrorReportingBase{su.errConn, item.dialName, item.DomainAddr, item.IpAddr}
			return c, ErrorReporting, nil
		} else {
			fmt.Printf("缓存连接失败:%v 代理:%v IP:%v err:%v \r\n", address, item.dialName, item.IpAddr, err)
		}
	}

	// 缓存未命中(缓存故障)时删除缓存记录
	su.connCache.Del(address)

	// 缓存未命中时同时使用多个线路尝试连接。
	resChan := make(chan dialTimeoutRes)
	connChan := make(chan netchan.ConnRes, 10)
	exitChan := make(chan int)
	// 选定连接 退出函数时不再尝试新的连接
	defer func() {
		defer func() { _ = recover() }()
		close(exitChan)
	}()

	// 另开一个线程进行连接并整理连接信息
	go func() {
		// 循环使用各个 upstream 进行连接
		dc, edit := su.dialClients.Get(address)
		sw := sync.WaitGroup{}
		sw.Add(len(dc))
		for _, d := range dc {
			d := d
			go func() {
				defer func() { sw.Done() }()

				// 未使用黑白名单时连接前执行延迟
				if edit == false && d.sleep != 0 {
					time.Sleep(d.sleep)
				}
				select {
				case <-exitChan:
					return
				default:
				}

				userData := chanDialTimeoutUserData{d.name, address, d}
				cerr := netchan.ChanDialTimeout(d.pc, d.dialCredit, connChan, exitChan, d.dnsResolve, &userData, nil, network, address, timeout)
				if cerr != nil {
					log.Printf("线路 %v 连接 %v 失败,错误:%v", d.name, address, cerr)
				}
			}()
		}

		// 所有连接线程都结束时关闭 connChan 信道
		// 终止取结果的线程,防止永久阻塞。
		go func() {
			sw.Wait()
			close(connChan)
		}()

		// 取结果
		// 将最快建立的结果返回给 resChan 好返回主函数。
		// 在无法建立连接时将返回err
		go func() {
			ok := false         // 是否已经找到最快的稳定连接
			var oConn net.Conn  // 保存最快的结果,如果全部的连接都有问题时间使用这个连接
			oConnClose := false //oConn 是否需要关闭。
			oConnCloseMutex := sync.Mutex{}
			var ErrorReporting *UpStreamErrorReportingBase //错误报告(实际使用 oconn 连接如果发现问题通过本变量报告错误)
			var oconnTimeout *time.Timer                   // oconn 定时器

			defer func() {
				// 结束时函数关闭之前保存的最快的连接。
				oConnCloseMutex.Lock()
				defer oConnCloseMutex.Unlock()
				if oConnClose == true {
					oConn.Close()
				}
			}()

			for conn := range connChan {
				// 保存最快的连接(可能并不稳定) 用于应付找不到稳定连接的情况
				conn := conn
				userData := conn.UserData.(*chanDialTimeoutUserData)

				// 上报这个连接建立的速度
				go su.connCache.Updata(userData.domainAddr, conn.IpAddr, conn.Ping+userData.dialClient.correctDelay, userData.dialClient, userData.dialName)

				// 返回最快并稳定的连接
				if ok == false && su.errConn.Check(userData.dialName, userData.domainAddr, conn.IpAddr) == true {
					// 找到了最快的稳定连接
					ok = true
					ErrorReporting = &UpStreamErrorReportingBase{su.errConn, userData.dialName, userData.domainAddr, conn.IpAddr}
					if oconnTimeout != nil {
						oconnTimeout.Stop()
					}
					func() {
						defer func() { _ = recover() }()
						resChan <- dialTimeoutRes{conn.Conn, ErrorReporting, nil}
					}()
					log.Printf("为 %v 找到了最快的稳定连接 %v ,线路:%v.\r\n", userData.domainAddr, conn.IpAddr, userData.dialName)
				} else {
					// 已经有最快稳定链接 或者 本连接不稳定。

					// 未安全返回时保存最快的一个连接
					if oConn == nil && ok == false {
						// 如果未找到可靠连接并且本连接时最快建立的连接 就先保存下本连接等待备用。
						oConn = conn.Conn
						oConnClose = true
						ErrorReporting = &UpStreamErrorReportingBase{su.errConn, userData.dialName, userData.domainAddr, conn.IpAddr}

						// 如果一定时间内还没找到最快的稳定线路那么就是用这个线路,并且不再尝试新连接。
						oconnTimeout = time.AfterFunc(1*time.Second, func() {
							defer func() { _ = recover() }()
							oConnCloseMutex.Lock()
							defer oConnCloseMutex.Unlock()
							resChan <- dialTimeoutRes{oConn, ErrorReporting, nil}
							oConnClose = false
						})

					} else {
						conn.Conn.Close()
					}
				}
			}

			// 最后如果连接未建立,并且有最快建立的连接,那么即使他不稳定也是用这个。
			if ok == false && oConn != nil {
				func() {
					defer func() { recover() }()
					oConnCloseMutex.Lock()
					defer oConnCloseMutex.Unlock()
					resChan <- dialTimeoutRes{oConn, ErrorReporting, nil}
					oConnClose = false
					log.Printf("为 %v 找到了最快但不稳定连接 %v ,线路:%v.\r\n", ErrorReporting.DomainAddr, ErrorReporting.IpAddr, ErrorReporting.DailName)
				}()
				return
			}

			// 最后还是没找到可用连接
			if ok == false {
				func() {
					defer func() { recover() }()
					resChan <- dialTimeoutRes{nil, nil, fmt.Errorf("所有线路建立连接失败。")}
				}()
			}
		}()
	}()

	res := <-resChan
	close(resChan)
	return res.conn, res.errReporting, res.err
}