channel.go
func (ch *Channel) Connect(ctx context.Context, hostPort string) (*Connection, error) {
	switch state := ch.State(); state {
	case ChannelClient, ChannelListening:
		break
	default:
		ch.log.Debugf("Connect rejecting new connection as state is %v", state)
		return nil, errInvalidStateForOp
	}

	// The context timeout applies to the whole call, but users may want a lower
	// connect timeout (e.g. for streams).
	if params := getTChannelParams(ctx); params != nil && params.connectTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, params.connectTimeout)
		defer cancel()
	}

	events := connectionEvents{
		OnActive:           ch.outboundConnectionActive,
		OnCloseStateChange: ch.connectionCloseStateChange,
		OnExchangeUpdated:  ch.exchangeUpdated,
	}

	if err := ctx.Err(); err != nil {
		return nil, GetContextError(err)
	}

	timeout := getTimeout(ctx)
	tcpConn, err := dialContext(ctx, hostPort)
	if err != nil {
		if ne, ok := err.(net.Error); ok && ne.Timeout() {
			ch.log.WithFields(
				LogField{"remoteHostPort", hostPort},
				LogField{"timeout", timeout},
			).Info("Outbound net.Dial timed out.")
			err = ErrTimeout
		} else if ctx.Err() == context.Canceled {
			ch.log.WithFields(
				LogField{"remoteHostPort", hostPort},
			).Info("Outbound net.Dial was cancelled.")
			err = GetContextError(ErrRequestCancelled)
		} else {
			ch.log.WithFields(
				ErrField(err),
				LogField{"remoteHostPort", hostPort},
			).Info("Outbound net.Dial failed.")
		}
		return nil, err
	}

	conn, err := ch.outboundHandshake(ctx, tcpConn, hostPort, events)
	if conn != nil {
		// It's possible that the connection we just created responds with a host:port
		// that is not what we tried to connect to. E.g., we may have connected to
		// 127.0.0.1:1234, but the returned host:port may be 10.0.0.1:1234.
		// In this case, the connection won't be added to 127.0.0.1:1234 peer
		// and so future calls to that peer may end up creating new connections. To
		// avoid this issue, and to avoid clients being aware of any TCP relays, we
		// add the connection to the intended peer.
		if hostPort != conn.remotePeerInfo.HostPort {
			conn.log.Debugf("Outbound connection host:port mismatch, adding to peer %v", conn.remotePeerInfo.HostPort)
			ch.addConnectionToPeer(hostPort, conn, outbound)
		}
	}

	return conn, err
}
func (ch *Channel) Connect(ctx context.Context, hostPort string) (*Connection, error) {
	switch state := ch.State(); state {
	case ChannelClient, ChannelListening:
		break
	default:
		ch.log.Debugf("Connect rejecting new connection as state is %v", state)
		return nil, errInvalidStateForOp
	}

	// The context timeout applies to the whole call, but users may want a lower
	// connect timeout (e.g. for streams).
	if params := getTChannelParams(ctx); params != nil && params.connectTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, params.connectTimeout)
		defer cancel()
	}

	events := connectionEvents{
		OnActive:           ch.outboundConnectionActive,
		OnCloseStateChange: ch.connectionCloseStateChange,
		OnExchangeUpdated:  ch.exchangeUpdated,
	}

	if err := ctx.Err(); err != nil {
		return nil, GetContextError(err)
	}

	timeout := getTimeout(ctx)
	tcpConn, err := dialContext(ctx, hostPort)
	if err != nil {
		if ne, ok := err.(net.Error); ok && ne.Timeout() {
			ch.log.WithFields(
				LogField{"remoteHostPort", hostPort},
				LogField{"timeout", timeout},
			).Info("Outbound net.Dial timed out.")
			err = ErrTimeout
		} else if ctx.Err() == context.Canceled {
			ch.log.WithFields(
				LogField{"remoteHostPort", hostPort},
			).Info("Outbound net.Dial was cancelled.")
			err = GetContextError(ErrRequestCancelled)
		} else {
			ch.log.WithFields(
				ErrField(err),
				LogField{"remoteHostPort", hostPort},
			).Info("Outbound net.Dial failed.")
		}
		return nil, err
	}

	conn, err := ch.outboundHandshake(ctx, tcpConn, hostPort, events)
	if conn != nil {
		// It's possible that the connection we just created responds with a host:port
		// that is not what we tried to connect to. E.g., we may have connected to
		// 127.0.0.1:1234, but the returned host:port may be 10.0.0.1:1234.
		// In this case, the connection won't be added to 127.0.0.1:1234 peer
		// and so future calls to that peer may end up creating new connections. To
		// avoid this issue, and to avoid clients being aware of any TCP relays, we
		// add the connection to the intended peer.
		if hostPort != conn.remotePeerInfo.HostPort {
			conn.log.Debugf("Outbound connection host:port mismatch, adding to peer %v", conn.remotePeerInfo.HostPort)
			ch.addConnectionToPeer(hostPort, conn, outbound)
		}
	}

	return conn, err
}
// Connect creates a new outbound connection to hostPort.