/// <summary>
/// Connect to the given IP address using the port specified as part of the network parameters. Once construction
/// is complete a functioning network channel is set up and running.
/// </summary>
/// <param name="peerAddress">IP address to connect to. IPv6 is not currently supported by BitCoin. If port is not positive the default port from params is used.</param>
/// <param name="params">Defines which network to connect to and details of the protocol.</param>
/// <param name="bestHeight">How many blocks are in our best chain</param>
/// <param name="connectTimeout">Timeout in milliseconds when initially connecting to peer</param>
/// <exception cref="IOException">If there is a network related failure.</exception>
/// <exception cref="ProtocolException">If the version negotiation failed.</exception>
public NetworkConnection(PeerAddress peerAddress, NetworkParameters @params, uint bestHeight, int connectTimeout)
{
_params = @params;
_remoteIp = peerAddress.Addr;
var port = (peerAddress.Port > 0) ? peerAddress.Port : @params.Port;
var address = new IPEndPoint(_remoteIp, port);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(address);
_socket.SendTimeout = _socket.ReceiveTimeout = connectTimeout;
_out = new NetworkStream(_socket, FileAccess.Write);
_in = new NetworkStream(_socket, FileAccess.Read);
// the version message never uses check-summing. Update check-summing property after version is read.
_serializer = new BitcoinSerializer(@params, false);
// Announce ourselves. This has to come first to connect to clients beyond v0.30.20.2 which wait to hear
// from us until they send their version message back.
WriteMessage(new VersionMessage(@params, bestHeight));
// When connecting, the remote peer sends us a version message with various bits of
// useful data in it. We need to know the peer protocol version before we can talk to it.
_versionMessage = (VersionMessage)ReadMessage();
// Now it's our turn ...
// Send an ACK message stating we accept the peers protocol version.
WriteMessage(new VersionAck());
// And get one back ...
ReadMessage();
// Switch to the new protocol version.
var peerVersion = _versionMessage.ClientVersion;
_log.InfoFormat("Connected to peer: version={0}, subVer='{1}', services=0x{2:X}, time={3}, blocks={4}",
peerVersion,
_versionMessage.SubVer,
_versionMessage.LocalServices,
UnixTime.FromUnixTime(_versionMessage.Time),
_versionMessage.BestHeight
);
// BitCoinSharp is a client mode implementation. That means there's not much point in us talking to other client
// mode nodes because we can't download the data from them we need to find/verify transactions.
if (!_versionMessage.HasBlockChain())
{
// Shut down the socket
try
{
Shutdown();
}
catch (IOException)
{
// ignore exceptions while aborting
}
throw new ProtocolException("Peer does not have a copy of the block chain.");
}
// newer clients use check-summing
_serializer.UseChecksumming(peerVersion >= 209);
// Handshake is done!
}