/// <summary>
/// Asynchronous TCP client
/// </summary>
public class AsyncTcpClient : IDisposable
{
#region Fields
private TcpClient tcpClient;
private bool disposed = false;
private int retries = 0;
#endregion
#region Ctors
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteEP">Remote server endpoint</param>
public AsyncTcpClient(IPEndPoint remoteEP)
: this(new[] { }, )
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteEP">Remote server endpoint</param>
/// <param name="localEP">Local client endpoint</param>
public AsyncTcpClient(IPEndPoint remoteEP, IPEndPoint localEP)
: this(new[] { }, , localEP)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteIPAddress">Remote server IP address</param>
/// <param name="remotePort">Remote Server Port</param>
public AsyncTcpClient(IPAddress remoteIPAddress, int remotePort)
: this(new[] { remoteIPAddress }, remotePort)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteIPAddress">Remote server IP address</param>
/// <param name="remotePort">Remote Server Port</param>
/// <param name="localEP">Local client endpoint</param>
public AsyncTcpClient(
IPAddress remoteIPAddress, int remotePort, IPEndPoint localEP)
: this(new[] { remoteIPAddress }, remotePort, localEP)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteHostName">Remote server hostname</param>
/// <param name="remotePort">Remote Server Port</param>
public AsyncTcpClient(string remoteHostName, int remotePort)
: this((remoteHostName), remotePort)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteHostName">Remote server hostname</param>
/// <param name="remotePort">Remote Server Port</param>
/// <param name="localEP">Local client endpoint</param>
public AsyncTcpClient(
string remoteHostName, int remotePort, IPEndPoint localEP)
: this((remoteHostName), remotePort, localEP)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteIPAddresses">Remote server IP address list</param>
/// <param name="remotePort">Remote Server Port</param>
public AsyncTcpClient(IPAddress[] remoteIPAddresses, int remotePort)
: this(remoteIPAddresses, remotePort, null)
{
}
/// <summary>
/// Asynchronous TCP client
/// </summary>
/// <param name="remoteIPAddresses">Remote server IP address list</param>
/// <param name="remotePort">Remote Server Port</param>
/// <param name="localEP">Local client endpoint</param>
public AsyncTcpClient(
IPAddress[] remoteIPAddresses, int remotePort, IPEndPoint localEP)
{
= remoteIPAddresses;
= remotePort;
= localEP;
= ;
if ( != null)
{
= new TcpClient();
}
else
{
= new TcpClient();
}
Retries = 3;
RetryInterval = 5;
}
#endregion
#region Properties
/// <summary>
/// Whether a connection has been established with the server
/// </summary>
public bool Connected { get { return ; } }
/// <summary>
/// List of IP addresses of remote servers
/// </summary>
public IPAddress[] Addresses { get; private set; }
/// <summary>
/// The port of the remote server
/// </summary>
public int Port { get; private set; }
/// <summary>
/// Number of connection retry times
/// </summary>
public int Retries { get; set; }
/// <summary>
/// Connection retry interval
/// </summary>
public int RetryInterval { get; set; }
/// <summary>
/// Remote server endpoint
/// </summary>
public IPEndPoint RemoteIPEndPoint
{
get { return new IPEndPoint(Addresses[0], Port); }
}
/// <summary>
/// Local client endpoint
/// </summary>
protected IPEndPoint LocalIPEndPoint { get; private set; }
/// <summary>
/// The encoding used for communication
/// </summary>
public Encoding Encoding { get; set; }
#endregion
#region Connect
/// <summary>
///Connect to the server
/// </summary>
/// <returns>Async TCP Client</returns>
public AsyncTcpClient Connect()
{
if (!Connected)
{
// start the async connect operation
(
Addresses, Port, HandleTcpServerConnected, tcpClient);
}
return this;
}
/// <summary>
/// Close the connection to the server
/// </summary>
/// <returns>Async TCP Client</returns>
public AsyncTcpClient Close()
{
if (Connected)
{
retries = 0;
();
RaiseServerDisconnected(Addresses, Port);
}
return this;
}
#endregion
#region Receive
private void HandleTcpServerConnected(IAsyncResult ar)
{
try
{
(ar);
RaiseServerConnected(Addresses, Port);
retries = 0;
}
catch (Exception ex)
{
(ex);
if (retries > 0)
{
((,
"Connect to server with retry {0} failed.", retries));
}
retries++;
if (retries > Retries)
{
// we have failed to connect to all the IP Addresses,
// connection has failed overall.
RaiseServerExceptionOccurred(Addresses, Port, ex);
return;
}
else
{
((,
"Waiting {0} seconds before retrying to connect to server.",
RetryInterval));
((RetryInterval));
Connect();
return;
}
}
// we are connected successfully and start asyn read operation.
byte[] buffer = new byte[];
().BeginRead(
buffer, 0, , HandleDatagramReceived, buffer);
}
private void HandleDatagramReceived(IAsyncResult ar)
{
NetworkStream stream = ();
int numberOfReadBytes = 0;
try
{
numberOfReadBytes = (ar);
}
catch
{
numberOfReadBytes = 0;
}
if (numberOfReadBytes == 0)
{
// connection has been closed
Close();
return;
}
// received byte and trigger event notification
byte[] buffer = (byte[]);
byte[] receivedBytes = new byte[numberOfReadBytes];
(buffer, 0, receivedBytes, 0, numberOfReadBytes);
RaiseDatagramReceived(tcpClient, receivedBytes);
RaisePlaintextReceived(tcpClient, receivedBytes);
// then start reading from the network again
(
buffer, 0, , HandleDatagramReceived, buffer);
}
#endregion
#region Events
/// <summary>
/// Received data packet event
/// </summary>
public event EventHandler<TcpDatagramReceivedEventArgs<byte[]>> DatagramReceived;
/// <summary>
/// Received the data packet civilization event
/// </summary>
public event EventHandler<TcpDatagramReceivedEventArgs<string>> PlaintextReceived;
private void RaiseDatagramReceived(TcpClient sender, byte[] datagram)
{
if (DatagramReceived != null)
{
DatagramReceived(this,
new TcpDatagramReceivedEventArgs<byte[]>(sender, datagram));
}
}
private void RaisePlaintextReceived(TcpClient sender, byte[] datagram)
{
if (PlaintextReceived != null)
{
PlaintextReceived(this,
new TcpDatagramReceivedEventArgs<string>(
sender, (datagram, 0, )));
}
}
/// <summary>
/// The connection to the server has been established
/// </summary>
public event EventHandler<TcpServerConnectedEventArgs> ServerConnected;
/// <summary>
/// The connection to the server has been disconnected event
/// </summary>
public event EventHandler<TcpServerDisconnectedEventArgs> ServerDisconnected;
/// <summary>
/// An exception event occurred when the connection to the server
/// </summary>
public event EventHandler<TcpServerExceptionOccurredEventArgs> ServerExceptionOccurred;
private void RaiseServerConnected(IPAddress[] ipAddresses, int port)
{
if (ServerConnected != null)
{
ServerConnected(this,
new TcpServerConnectedEventArgs(ipAddresses, port));
}
}
private void RaiseServerDisconnected(IPAddress[] ipAddresses, int port)
{
if (ServerDisconnected != null)
{
ServerDisconnected(this,
new TcpServerDisconnectedEventArgs(ipAddresses, port));
}
}
private void RaiseServerExceptionOccurred(
IPAddress[] ipAddresses, int port, Exception innerException)
{
if (ServerExceptionOccurred != null)
{
ServerExceptionOccurred(this,
new TcpServerExceptionOccurredEventArgs(
ipAddresses, port, innerException));
}
}
#endregion
#region Send
/// <summary>
/// Send message
/// </summary>
/// <param name="datagram">Message</param>
public void Send(byte[] datagram)
{
if (datagram == null)
throw new ArgumentNullException("datagram");
if (!Connected)
{
RaiseServerDisconnected(Addresses, Port);
throw new InvalidProgramException(
"This client has not connected to server.");
}
().BeginWrite(
datagram, 0, , HandleDatagramWritten, tcpClient);
}
private void HandleDatagramWritten(IAsyncResult ar)
{
((TcpClient)).GetStream().EndWrite(ar);
}
/// <summary>
/// Send message
/// </summary>
/// <param name="datagram">Message</param>
public void Send(string datagram)
{
Send((datagram));
}
#endregion
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed
/// and unmanaged resources; <c>false</c>
/// to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing)
{
if (!)
{
if (disposing)
{
try
{
Close();
if (tcpClient != null)
{
tcpClient = null;
}
}
catch (SocketException ex)
{
(ex);
}
}
disposed = true;
}
}
#endregion
}