using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace RDavey.Net
{
public class AsyncTcpClient
{
private IPAddress[] addresses;
private int port;
private WaitHandle addressesSet;
private TcpClient tcpClient;
private int failedConnectionCount;
///
/// Construct a new client from a known IP Address
///
/// The IP Address of the server
/// The port of the server
public AsyncTcpClient(IPAddress address, int port)
: this(new[] { address }, port)
{
}
///
/// Construct a new client where multiple IP Addresses for
/// the same client are known.
///
/// The array of known IP Addresses
/// The port of the server
public AsyncTcpClient(IPAddress[] addresses, int port)
: this(port)
{
this.addresses = addresses;
}
///
/// Construct a new client where the address or host name of
/// the server is known.
///
/// The host name or address of the server
/// The port of the server
public AsyncTcpClient(string hostNameOrAddress, int port)
: this(port)
{
addressesSet = new AutoResetEvent(false);
Dns.BeginGetHostAddresses(hostNameOrAddress, GetHostAddressesCallback, null);
}
///
/// Private constuctor called by other constuctors
/// for common operations.
///
///
private AsyncTcpClient(int port)
{
if (port < 0)
throw new ArgumentException();
this.port = port;
this.tcpClient = new TcpClient();
this.Encoding = Encoding.Default;
}
///
/// The endoding used to encode/decode string when sending and receiving.
///
public Encoding Encoding { get; set; }
///
/// Attempts to connect to one of the specified IP Addresses
///
public void Connect()
{
if (addressesSet != null)
//Wait for the addresses value to be set
addressesSet.WaitOne();
//Set the failed connection count to 0
Interlocked.Exchange(ref failedConnectionCount, 0);
//Start the async connect operation
tcpClient.BeginConnect(addresses, port, ConnectCallback, null);
}
///
/// Writes a string to the network using the defualt encoding.
///
/// The string to write
///
/// when the write operation has completed.
public void Write(string data)
{
byte[] bytes = Encoding.GetBytes(data);
Write(bytes);
}
///
/// Writes an array of bytes to the network.
///
/// The array to write
///
/// when the write operation has completed.
public void Write(byte[] bytes)
{
NetworkStream networkStream = tcpClient.GetStream();
//Start async write operation
networkStream.BeginWrite(bytes, 0, bytes.Length, WriteCallback, null);
}
///
/// Callback for Write operation
///
/// The AsyncResult object
private void WriteCallback(IAsyncResult result)
{
NetworkStream networkStream = tcpClient.GetStream();
networkStream.EndWrite(result);
}
///
/// Callback for Connect operation
///
/// The AsyncResult object
private void ConnectCallback(IAsyncResult result)
{
try
{
tcpClient.EndConnect(result);
}
catch
{
//Increment the failed connection count in a thread safe way
Interlocked.Increment(ref failedConnectionCount);
if (failedConnectionCount >= addresses.Length)
{
//We have failed to connect to all the IP Addresses
//connection has failed overall.
return;
}
}
//We are connected successfully.
NetworkStream networkStream = tcpClient.GetStream();
byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
//Now we are connected start asyn read operation.
networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
}
///
/// Callback for Read operation
///
/// The AsyncResult object
private void ReadCallback(IAsyncResult result)
{
int read;
NetworkStream networkStream;
try
{
networkStream = tcpClient.GetStream();
read = networkStream.EndRead(result);
}
catch
{
//An error has occured when reading
return;
}
if (read == 0)
{
//The connection has been closed.
return;
}
byte[] buffer = result.AsyncState as byte[];
string data = this.Encoding.GetString(buffer, 0, read);
//Do something with the data object here.
//Then start reading from the network again.
networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
}
///
/// Callback for Get Host Addresses operation
///
/// The AsyncResult object
private void GetHostAddressesCallback(IAsyncResult result)
{
addresses = Dns.EndGetHostAddresses(result);
//Signal the addresses are now set
((AutoResetEvent)addressesSet).Set();
}
}
}