I've written my own TCPClient and TCPServer class components to handle traffic over the network between applications.
When I test the components locally they work. In the local network between different machines they also work.
When I try to connect to a remote location that's added to our network with Tunnels, I'm receiving a Timeout on the connect, but a ping to the IP from commandline works fine without a problem.
There's a firewall between the 2 locations, but the ports used during the connection have been opened. Yet the timeout keeps occuring. Is there anything special I need to do ?
Client Code:
Expand|Select|Wrap|Line Numbers
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- /* Components API */
- using Components;
- using Components.EventArgs;
- namespace Components.Network.Clients
- {
- /// <summary>
- /// This class is a direct implementation of the IClient interface and provides a concrete
- /// implementation using the TCP protocol and Berkley sockets as it's base components.
- /// </summary>
- public class TCPClient : IClient
- {
- #region Constructors
- /// <summary>
- /// Creates a new instance of the TCPClient class and initializes the internal components
- /// of the class to be ready for establishing a connection to a remote location.
- /// The client is considered 'disconnected' and is not actively listening for data.
- /// </summary>
- /// <param name="address">The remote ip address to establish a connection with.</param>
- /// <param name="port">The remote port to establish a connection with.</param>
- public TCPClient(string remoteAddress, int remotePort)
- {
- // Save the parameters into the local fields and initialize the internal
- // members of our class.
- m_lock = new object();
- m_socket = null;
- m_backgroundthread = null;
- m_running = false;
- m_local_endpoint = new IPEndPoint(IPAddress.Any, 0);
- m_remote_endpoint = new IPEndPoint(IPAddress.Loopback, 0);
- RemoteAddress = remoteAddress;
- RemotePort = remotePort;
- Asynchronous = true;
- Connected = false;
- }
- /// <summary>
- /// Creates a new instance of the TCPClient class with the socket that has been accepted by
- /// an IServer implementation. The client assumes the socket to be following the TCP protocol,
- /// but will not validate this.
- /// The internal components of the TCPClient are automaticly configured and the client is considered
- /// 'connected' and will handle incoming data.
- /// </summary>
- /// <param name="socket">The Socket accepted by the IServer implementation.</param>
- public TCPClient(Socket socket)
- {
- // Save the parameters into the local datamembers and extract
- // the required information from the socket.
- m_lock = new object();
- m_backgroundthread = null;
- Asynchronous = true;
- m_socket = socket;
- m_socket.NoDelay = true;
- m_local_endpoint = (socket.LocalEndPoint as IPEndPoint);
- m_remote_endpoint = (socket.RemoteEndPoint as IPEndPoint);
- Connected = socket.Connected;
- // Initialize the internal background thread, because we assume the socket
- // to be connected.
- InitializeBackgroundThread();
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Determines if two IClient objects are equall. IClient objects are considered
- /// equal if they connect to the same endpoint.
- /// </summary>
- /// <param name="other">The other IClient to compare against.</param>
- /// <returns>True if both IClients are considered equall.</returns>
- public bool Equals(IClient other)
- {
- return string.Equals(this.RemoteAddress, other.RemoteAddress) && (this.RemotePort == other.RemotePort);
- }
- /// <summary>
- /// Establishes a connection to a remote location and initializes the internal
- /// components to process incomming data.
- /// </summary>
- /// <returns>The reference to the IClient implementation.</returns>
- /// <remarks>If the connection attempt succeeds, the Connected property will be set to true; otherwise false.</remarks>
- public IClient Connect()
- {
- // Claim the lock on the socket, so we have exclusive access to the socket during the
- // connection attempt.
- lock (m_lock)
- {
- // Check if the socket is already connected. If a connection has already been
- // established, do not try to connect again.
- if (!Connected)
- {
- // Surround the entire connection attempt with a try-catch statement to prevent
- // errors from breaking the code.
- try
- {
- // Create a new instance of the Berkley socket that relies on the InterNetwork for
- // IPv4 connections and uses the TCP Streaming protocol for communication.
- m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- // Bind the socket to the local endpoint specified. Depending on the settings made by the
- // user this is a specific ip and port or a random selection by the OS.
- m_socket.Bind(m_local_endpoint);
- // Establish a connection to the remote endpoint. This is determined by the settings
- // made by the user.
- m_socket.Connect(m_remote_endpoint);
- // Configure the socket to ignore the Naggle algorithm when sending data.
- m_socket.NoDelay = true;
- // Initialize the background thread to properly handle incoming data from the socket.
- InitializeBackgroundThread();
- // Set the Connected flag to true.
- Connected = true;
- // Raise the onConnected event.
- RaiseOnConnectEvent(RemoteAddress, RemotePort);
- }
- catch (Exception ex)
- {
- // Raise the onError event first.
- RaiseOnErrorEvent("Connect", ex);
- // Call the disconnect routine.
- Disconnect();
- }
- }
- }
- // Return the reference to ourselves so we can link function calls.
- return this;
- }
- /// <summary>
- /// Terminates the connection with a remote location and uninitializes the internal
- /// components that process incoming data.
- /// </summary>
- /// <remarks>The connected property is always set to false regardless of the success of the function.</remarks>
- public IClient Disconnect()
- {
- // Obtain the lock so we have exlusive access to the entire socket while performing this operation.
- lock (m_lock)
- {
- // Surround the entire disconnect operation with a try-catch statement. This prevents errors from
- // breaking the code.
- try
- {
- // Check if the socket is connected. If the socket is not connected, then we
- // don't have to do anything.
- if (Connected)
- {
- // First we'll stop the background thread in a proper fashion.
- UninitializeBackgroundThread();
- // Close the connection of the socket, but make sure we don't dispose the
- // internal data of the socket. We only wish to terminate the connection
- // so that the socket can be reused at a later point.
- m_socket.Shutdown(SocketShutdown.Both);
- m_socket.Close();
- m_socket = null;
- // Raise the OnDisconnected event.
- RaiseOnDisconnectEvent(RemoteAddress, RemotePort);
- }
- // Always set the connected flag back to false.
- Connected = false;
- }
- catch (Exception ex)
- {
- // Raise the onError event with the given exception.
- RaiseOnErrorEvent("Disconnect", ex);
- }
- // Always set the connected property to false.
- Connected = false;
- }
- // Return a reference to ourselves so we link calls.
- return this;
- }
- /// <summary>
- /// Sends the data stored in the array over the socket to the remote endpoint. The function reports
- /// back the amount of bytes that have been send over the socket.
- /// </summary>
- /// <param name="data">A one-dimensional array holding the data to be sent.</param>
- /// <param name="length">The amount of bytes to send, starting at the first position in the array.</param>
- /// <returns>The amount of bytes that have been sent over the socket.</returns>
- /// <remarks>The function will return -1 if an error occurs and returns zero if no connection was available.</remarks>
- public int Send(byte[] data, int length)
- {
- // Obtain the lock on the socket, so we have exclusive access to the socket
- // while performing the send operation.
- lock (m_lock)
- {
- // Check if the connection is available. Return the amount of bytes beeing sent,
- // or return -1 in case of an error, or 0 in case of no connection.
- if (Connected)
- try { return m_socket.Send(data, length, SocketFlags.None); }
- catch (Exception ex) { RaiseOnErrorEvent("Send", ex); return -1; }
- else return 0;
- }
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the remote IP address of the connection.
- /// </summary>
- /// <exception cref="ArgumentException">The provided value cannot be cast into a valid IP address.</exception>
- public string RemoteAddress
- {
- get { return m_remote_endpoint.Address.ToString(); }
- set
- {
- IPAddress address;
- if (IPAddress.TryParse(value, out address)) m_remote_endpoint.Address = address;
- else throw new ArgumentException("The provided value cannot be cast into a valid IP address.");
- }
- }
- /// <summary>
- /// Gets or sets the local IP address of the connection.
- /// </summary>
- /// <exception cref="ArgumentException">The provided value cannot be cast into a valid IP address.</exception>
- public string LocalAddress
- {
- get { return m_local_endpoint.Address.ToString(); }
- set
- {
- IPAddress address;
- if (IPAddress.TryParse(value, out address)) m_local_endpoint.Address = address;
- else throw new ArgumentException("The provided value cannot be cast into a valid IP address.");
- }
- }
- /// <summary>
- /// Gets or sets the remote port of the connection.
- /// </summary>
- /// <exception cref="ArgumentException">The provided value is not a valid port.</exception>
- /// <remarks>Valid ports are any integer between 1 and 65535.</remarks>
- public int RemotePort
- {
- get { return m_remote_endpoint.Port; }
- set
- {
- if ((value < 1) || (value > ushort.MaxValue)) throw new ArgumentException("The provided value is not a valid port.");
- else m_remote_endpoint.Port = value;
- }
- }
- /// <summary>
- /// Gets or sets the local port of the connection.
- /// </summary>
- /// <exception cref="ArgumentException">The provided value is not a valid port.</exception>
- /// <remarks>Valid ports are any integer between 1 and 65535.</remarks>
- public int LocalPort
- {
- get { return m_local_endpoint.Port; }
- set
- {
- if ((value < 1) || (value > ushort.MaxValue)) throw new ArgumentException("The provided value is not a valid port.");
- else m_local_endpoint.Port = value;
- }
- }
- /// <summary>
- /// Gets a value indicating whether the IClient is connected.
- /// </summary>
- public bool Connected
- {
- get { return m_connected; }
- protected set { m_connected = value; }
- }
- /// <summary>
- /// Gets or sets a value indicating whether the events are fired asynchronous or not.
- /// </summary>
- /// <remarks>Asynchronous and Synchronous events are both raised in sequence, but Asynchronous Events do not wait for the previous event to finish.</remarks>
- public bool Asynchronous
- {
- get { return m_asynchronous; }
- set { m_asynchronous = value; }
- }
- #endregion
- #region Events
- /// <summary>
- /// This event gets raised when the IClient implementation receives data on
- /// the internal socket.
- /// </summary>
- public event EventHandler<DiagramArguments> OnData
- {
- add { m_event_data += value; }
- remove { m_event_data -= value; }
- }
- /// <summary>
- /// This event gets raised when the IClient implementation establishes
- /// a connection to a remote endpoint.
- /// </summary>
- public event EventHandler<ConnectionArguments> OnConnect
- {
- add { m_event_connect += value; }
- remove { m_event_connect -= value; }
- }
- /// <summary>
- /// This event gets raised when the IClient implementation terminates
- /// a connection from a remote endpoint.
- /// </summary>
- public event EventHandler<ConnectionArguments> OnDisconnect
- {
- add { m_event_disconnect += value; }
- remove { m_event_disconnect -= value; }
- }
- /// <summary>
- /// This event gets raised when the IClient implementation encounters an internal
- /// error during the execution of the functionality calls.
- /// </summary>
- public event EventHandler<ErrorArguments> OnError
- {
- add { m_event_error += value; }
- remove { m_event_error -= value; }
- }
- #endregion
- #region Private Methods
- /// <summary>
- /// Initializes the internal background thread of the IClient implementation. The thread
- /// polls the socket for incoming data and raises the onData event when the connection is
- /// established.
- /// </summary>
- /// <returns>True if the background thread is properly initialized; otherwise false.</returns>
- protected virtual bool InitializeBackgroundThread()
- {
- // Surround the entire initialization procedure with a try-catch statement
- // to trace errors in the code and to prevent these errors from breaking
- // the calling code.
- try
- {
- // Create a new instance of the Thread class and configure the instance to
- // work with our internal listen routine.
- m_backgroundthread = new Thread(new ThreadStart(ListenRoutine));
- // Configure the thread to work as a background thread, and to run
- // independently from the application.
- m_backgroundthread.IsBackground = true;
- m_backgroundthread.Name = string.Format("<TCPClient:{0}:{1}:ListenThread>", LocalAddress, LocalPort);
- m_backgroundthread.Start();
- // Return true since the thread has been properly initialized.
- return true;
- }
- catch (Exception ex)
- {
- // Raise the onError event with the given exception.
- RaiseOnErrorEvent("InitializeBackgroundThread", ex);
- // Return false since we could not properly initialize
- // the background thread.
- return false;
- }
- }
- /// <summary>
- /// Stops the internal background thread in a proper fashion, allowing the thread
- /// to finish it's last cycle over the internal socket if possible and then gracefully
- /// exiting the loop.
- /// </summary>
- protected virtual void UninitializeBackgroundThread()
- {
- // Stop the background thread by setting the running flag back to
- // false.
- m_running = false;
- // Nullify the reference to the background thread.
- m_backgroundthread = null;
- }
- /// <summary>
- /// This function is ran from the internal background thread of the IClient implementation
- /// and has the responsebility of polling the underlying socket for incoming data.
- /// If there is data available on the socket, the function will read the entire data into
- /// a single buffer and raise the onData event.
- /// </summary>
- protected virtual void ListenRoutine()
- {
- // Set our internal running flag to true so we can keep looping the thread for
- // as long as required. Calling the Disconnect function will terminate the
- // background thread.
- m_running = true;
- // start the thread loop.
- while (m_running)
- {
- // Surround the precise working of the process loop with a try-catch statement.
- // This allows us to catch errors and call the Disconnect function to properly
- // exit the loop.
- try
- {
- // Check if the socket is still available. If the socket is no longer available, raise an
- // exception.
- if (m_socket != null)
- {
- // Lock the socket here for exclusive access.
- lock (m_lock)
- {
- // Reevaluate the socket, connected property and the amount of data available.
- if ((m_socket != null) && Connected && (m_socket.Available > 0))
- {
- // Copy the data available into a seperate variable. This allows us to
- // perform a snapshot read operation on the socket and read the data
- // in bursts from the socket.
- int data_available = m_socket.Available;
- // Create the buffer that will hold the data for us.
- byte[] buffer = new byte[data_available];
- // Now perform the read operation on the socket to receive the data
- // that has been made available to us.
- int bytes_read = m_socket.Receive(buffer, 0, data_available, SocketFlags.None);
- // Raise the onData event with the information we just received.
- RaiseOnDataEvent(buffer, bytes_read, (m_socket.RemoteEndPoint as IPEndPoint).Address.ToString(), (m_socket.RemoteEndPoint as IPEndPoint).Port);
- }
- }
- // Sleep the thread for 10ms to prevent resource hogging of the backgroundthread
- Thread.Sleep(10);
- }
- else
- {
- // The internal socket is no longer available. Throw an exception so we can properly shutdown the
- // IClient implementation and terminate the background thread.
- throw new Exception("The internal socket has been closed/disposed.");
- }
- }
- catch (Exception ex)
- {
- // Raise the onError event.
- RaiseOnErrorEvent("ListenRoutine", ex);
- // Call the disconnect function to properly terminte the IClient and the background thread.
- // Calling the disconnect function will also raise the onDisconnect event.
- Disconnect();
- }
- }
- }
- /// <summary>
- /// Raises the onConnect event from the calling code. The event can be raised synchronously or asynchronously
- /// depending on the value indicated by the Asynchronous property of the class.
- /// </summary>
- /// <param name="address">The remote address of the connection.</param>
- /// <param name="port">The remote port of the connection.</param>
- /// <seealso cref="Asynchronous"/>
- protected virtual void RaiseOnConnectEvent(string address, int port)
- {
- // Copy the event handler first to a loca variable. This prevents
- // racing conditions between invoking the event and classes
- // unsubscribing from the event.
- EventHandler<ConnectionArguments> handle = m_event_connect;
- // Check if the handle has a value. If the handle is null, then no
- // other classes/functions are subscribed to this event.
- if (handle != null)
- {
- // Obtain the invocation list to raise the event for each subscriber.
- Delegate[] pointers = handle.GetInvocationList();
- // Loop through the entire list and invoke each event depending
- // on the settings. Surround the invoke with try-catch to catch
- // exception and not to interrupt the invokes.
- foreach (Delegate fpointer in pointers)
- {
- try
- {
- if (Asynchronous) (fpointer as EventHandler<ConnectionArguments>).BeginInvoke(this, new ConnectionArguments(address, port, this), new AsyncCallback(ConnectionCallback), null);
- else (fpointer as EventHandler<ConnectionArguments>)(this, new ConnectionArguments(address, port, this));
- }
- catch{ /* empty catch block */ }
- }
- }
- }
- /// <summary>
- /// Raises the onError event from the calling code. The event can be raised synchronously or asynchronously
- /// depending on the value indicated by the Asynchronous property of the class.
- /// </summary>
- /// <param name="functionName">The name of the function that has caused the error to occur.</param>
- /// <param name="message">The short simple error message related to the function.</param>
- /// <param name="innerMessage">The long detailed error message related to the function.</param>
- /// <param name="stackTrace">The stacktrace of the function at the time the error occured.</param>
- /// /// <remarks>When events are raised asynchronously, they are raised in sequence of each other, but the events do not wait for the previous event to finish. In synchronous operation, each event wait untill the previous event has finished.</remarks>
- protected virtual void RaiseOnErrorEvent(string functionName, string message, string innerMessage, string stackTrace)
- {
- // Copy the eventhandler to a local variable to eliminate racing conditions.
- EventHandler<Components.EventArgs.ErrorArguments> handle = m_event_error;
- // Check if there are subscribers for this event.
- if (handle != null)
- {
- // Obtain the invocation list to raise the event for each subscriber.
- Delegate[] handlers = handle.GetInvocationList();
- // Loop over each subsribed delegate.
- foreach (Delegate fpointer in handlers)
- {
- try
- {
- if (Asynchronous) (fpointer as EventHandler<ErrorArguments>).BeginInvoke(this, new ErrorArguments(functionName, message, innerMessage, stackTrace), new AsyncCallback(ErrorCallback), null);
- else (fpointer as EventHandler<ErrorArguments>)(this, new ErrorArguments(functionName, message, innerMessage, stackTrace));
- }
- catch { /* empty catch block */ }
- }
- }
- }
- /// <summary>
- /// Raises the onError event from the calling code. The event can be raised synchronously or asynchronously
- /// depending on the value indicated by the Aynchronous property of the class.
- /// </summary>
- /// <param name="functionName">The name of the function that has caused the error to occur.</param>
- /// <param name="ex">The exception that has been raised by the function.</param>
- /// <remarks>When events are raised asynchronously, they are raised in sequence of each other, but the events do not wait for the previous event to finish. In synchronous operation, each event wait untill the previous event has finished.</remarks>
- protected virtual void RaiseOnErrorEvent(string functionName, Exception ex)
- {
- RaiseOnErrorEvent(functionName, ex.Message, (ex.InnerException != null) ? ex.InnerException.Message : string.Empty, ex.StackTrace);
- }
- /// <summary>
- /// Raises the onData event from the calling code. The event can be raised synchronously or asynchronously
- /// depending on the value indicated by the Aynchronous property of the class.
- /// </summary>
- /// <param name="data">The raw binary data read from the socket.</param>
- /// <param name="size">The size of the data in the array.</param>
- /// <param name="address">The remote address the data came from.</param>
- /// <param name="port">The remote port the data came from.</param>
- protected virtual void RaiseOnDataEvent(byte[] data, int size, string address, int port)
- {
- // Copy the eventhandler to a local variable to eliminate racing conditions.
- EventHandler<EventArgs.DiagramArguments> handle = m_event_data;
- // Check if the handle has a value. If the handle is null, then no
- // other classes/functions are subscribed to this event.
- if (handle != null)
- {
- // Obtain the invocation list to raise the event for each subscriber.
- Delegate[] pointers = handle.GetInvocationList();
- // Loop through the entire list and invoke each event depending
- // on the settings. Surround the invoke with try-catch to catch
- // exception and not to interrupt the invokes.
- foreach (Delegate fpointer in pointers)
- {
- try
- {
- if (Asynchronous) (fpointer as EventHandler<DiagramArguments>).BeginInvoke(this, new DiagramArguments(data, size, new ConnectionArguments(address, port, this)), new AsyncCallback(DataCallback), null);
- else (fpointer as EventHandler<DiagramArguments>)(this, new DiagramArguments(data, size, new ConnectionArguments(address, port, this)));
- }
- catch { /* empty catch block to supress warnings. */ }
- }
- }
- }
- /// <summary>
- /// Raises the OnDisconnect event from the calling code. The event can be raised synchronously or asynchronously
- /// depending on the value indicated by the Aynchronous property of the class.
- /// </summary>
- /// <param name="address">The remote address of the connection.</param>
- /// <param name="port">The remote port of the connection.</param>
- protected virtual void RaiseOnDisconnectEvent(string address, int port)
- {
- // Copy the eventhandler to a local variable to eliminate racing conditions.
- EventHandler<ConnectionArguments> handle = m_event_disconnect;
- // Check if there are subscribers for the event.
- if (handle != null)
- {
- // Obtain the invocation list to raise the event for each subscriber.
- Delegate[] pointers = handle.GetInvocationList();
- // Loop through the entire list and invoke each event depending
- // on the settings. Surround the invoke with try-catch to catch
- // exception and not to interrupt the invokes.
- foreach (Delegate fpointer in pointers)
- {
- try
- {
- if (Asynchronous) (fpointer as EventHandler<ConnectionArguments>).BeginInvoke(this, new ConnectionArguments(address, port, this), new AsyncCallback(ConnectionCallback), null);
- else (fpointer as EventHandler<ConnectionArguments>)(this, new ConnectionArguments(address, port, this));
- }
- catch { /* empty catch block to supress warnings. */ }
- }
- }
- }
- /// <summary>
- /// Cleans and releases all the resources held by the delegate that was used to invoke
- /// the OnConnect/OnDisconnect event asynchronously from the calling code.
- /// </summary>
- /// <param name="result">The IAsyncResult containing a reference to the delegate that has invoked the event.</param>
- protected virtual void ConnectionCallback(IAsyncResult result)
- {
- // Cast the IAsyncResult first to a concrete AsyncResult implementation. This allows
- // us to access the delegate that has the resources we need to clean.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
- // Access the delegate of the asyncresult and cast this to the same type of delegate
- // we used to invoke the result. After the cast, call the EndInvoke operation to
- // release all resources held by the delegate.
- (aresult.AsyncDelegate as EventHandler<EventArgs.ConnectionArguments>).EndInvoke(result);
- }
- /// <summary>
- /// Cleans and releases all the resources held by the delegate that was used to invoke
- /// the OnData event asynchronously from the calling code.
- /// </summary>
- /// <param name="result">The IAsyncResult containing a reference to the delegate that has invoked the event.</param>
- protected virtual void DataCallback(IAsyncResult result)
- {
- // Cast the IAsyncResult first to a concrete AsyncResult implementation. This allows
- // us to access the delegate that has the resources we need to clean.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
- // Access the delegate of the asyncresult and cast this to the same type of delegate
- // we used to invoke the result. After the cast, call the EndInvoke operation to
- // release all resources held by the delegate.
- (aresult.AsyncDelegate as EventHandler<EventArgs.DiagramArguments>).EndInvoke(result);
- }
- /// <summary>
- /// Cleans and releases all the resources held by the delegate that was used to invoke
- /// the onError event asynchronously from the calling code.
- /// </summary>
- /// <param name="result">The IAsyncResult containing a reference to the delegate that has invoked the event.</param>
- protected virtual void ErrorCallback(IAsyncResult result)
- {
- // Cast the IAsyncResult first to a concrete AsyncResult implementation. This allows
- // us to access the delegate that has the resources we need to clean.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
- // Access the delegate of the asyncresult and cast this to the same type of delegate
- // we used to invoke the result. After the cast, call the EndInvoke operation to
- // release all resources held by the delegate.
- (aresult.AsyncDelegate as EventHandler<Components.EventArgs.ErrorArguments>).EndInvoke(result);
- }
- #endregion
- #region Private Fields
- EventHandler<DiagramArguments> m_event_data; // EventHandler to invoke the OnDisconnect event.
- EventHandler<ConnectionArguments> m_event_connect; // EventHandler to invoke the OnConnect event.
- EventHandler<ConnectionArguments> m_event_disconnect; // EventHandler to invoke the OnDisconnect event.
- EventHandler<ErrorArguments> m_event_error; // The Eventhandler to invoke the onError event.
- Socket m_socket; // The Berkley socket for TCP communications.
- Thread m_backgroundthread; // The background thread that listens for incoming data.
- readonly object m_lock; // Lock for multithreaded access to the component.
- IPEndPoint m_local_endpoint; // The local endpoint to establish a connection from.
- IPEndPoint m_remote_endpoint; // The remote endpoint to establish a connection with.
- bool m_asynchronous; // Value indicating whether our object is running asynchronously.
- bool m_running; // Value for controlling the background thread.
- bool m_connected; // Value indicating whether or not we're connected.
- #endregion
- }
- }