The problem is that my server is not receiving data.
The code below are the various classes I designed around sockets. It will be big...
I have run the code with the debugger, and I see in the debug output that the client connects to the server, the events get fired and the disconnect event gets fired as well.
I get confirmation that the data is beeing sent from the client as well, because the function returns a value greater then 0, so the send operation succeeded. The server however never receives the data.
I will not post the interfaces, only the concrete code. I'll add a bit of explanation to each class where possible. I have the feeling that I forgot something somewhere, but I cannot pinpoint it. I'm hoping someone can throw a look at it and pinpoint it for me.
The code should compile if you remove the Interface classes, in case someone wants to test it himself.
1. The server code
Expand|Select|Wrap|Line Numbers
- namespace VTServer.IO
- {
- public class TCPServer : IServer
- {
- #region Constructor
- /// <summary>
- /// Creates a new instance of the TCPServer class and initializes the internal datamembers
- /// with the values of the supplied parameters.
- /// </summary>
- /// <param name="yarn">The yarn that will process and keep track of all the clients.</param>
- /// <param name="port">The port to use for the TCPServer to listen on.</param>
- /// <param name="maximumconnections">The maximum amount of connections allowed.</param>
- public TCPServer(Yarn yarn, int port, int maximumconnections)
- {
- Yarn = yarn;
- MaximumConnections = maximumconnections;
- Interval = 10;
- Port = port;
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the Yarn that holds all the IFibers beeing handled by the server.
- /// </summary>
- public Yarn Yarn
- {
- get { lock (m_lock_yarn) { return m_yarn; } }
- set { lock (m_lock_yarn) { m_yarn = value; } }
- }
- /// <summary>
- /// Gets or sets the maximum amount of connections supported by the server.
- /// </summary>
- public int MaximumConnections
- {
- get { return m_maxconnections; }
- set { m_maxconnections = value; }
- }
- /// <summary>
- /// Gets or sets the port used by the TCPServer to listen on for incoming connections.
- /// </summary>
- public int Port
- {
- get { return m_port; }
- set { m_port = value; }
- }
- /// <summary>
- /// Gets or sets the interval in ms to wait between subsequent listen routines.
- /// </summary>
- public int Interval
- {
- get { return m_interval; }
- set { m_interval = value; }
- }
- /// <summary>
- /// Gets or sets a value indicating if the TCPServer is listening for connections.
- /// </summary>
- public bool Running
- {
- get { return m_running; }
- set { m_running = value; }
- }
- #endregion
- #region Events
- /// <summary>
- /// This event gets fired when the server has accepted a client connection.
- /// </summary>
- public event EventHandler OnClientConnected
- {
- add { lock (m_lock_event) { m_event_connected += value; } }
- remove { lock (m_lock_event) { m_event_connected -= value; } }
- }
- /// <summary>
- /// This event gets fired when the client disconnects from the server.
- /// </summary>
- public event EventHandler OnClientDisconnected
- {
- add { lock (m_lock_event) { m_event_disconnected += value; } }
- remove { lock (m_lock_event) { m_event_disconnected -= value; } }
- }
- /// <summary>
- /// This event gets fired when a client sends data to the server.
- /// </summary>
- public event EventHandler<ClientDataEventArgs> OnClientData
- {
- add { lock (m_lock_event) { m_event_data += value; } }
- remove { lock (m_lock_event) { m_event_data -= value; } }
- }
- /// <summary>
- /// This event gets fired when an error occurs in the server.
- /// This event will be fired asynchronously from the executing code.
- /// </summary>
- public event EventHandler<ErrorEventArgs> OnError
- {
- add { lock (m_lock_event) { m_event_error += value; } }
- remove { lock (m_lock_event) { m_event_error -= value; } }
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Disposes of the TCPServer and all the internal resources.
- /// </summary>
- public void Dispose()
- {
- // Clean ourselves
- Dispose(true);
- // Supress the GC
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Starts the TCPServer to listen on the specified port with the interval for incoming connections
- /// over the socket.
- /// </summary>
- /// <returns>True if the server has been started on the socket; otherwise false.</returns>
- public bool StartListening()
- {
- if (!Running)
- {
- // Surround the entire Start routine with a try-catch block to prevent errors from
- // breaking the code.
- try
- {
- // The first step is to start the internal socket of the server component and listen
- // for incoming connections.
- m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- m_socket.Bind(new IPEndPoint(IPAddress.Any, Port));
- m_socket.Listen(MaximumConnections);
- // Now that the server is listening for incoming connections, we can start the
- // internal timer to process the incoming connections.
- m_timer_listen = new Timer(new TimerCallback(Listen), null, 0, Interval);
- // Now that everything is up and running, we can return true.
- Running = true;
- return true;
- }
- catch (ApplicationException ex)
- {
- // A problem occured while starting the server. Raise the onErrorevent if possible and
- // return false since the server could not be started as requested.
- RaiseOnErrorEvent(ex);
- return false;
- }
- }
- // Return true here cause the server is running.
- return true;
- }
- /// <summary>
- /// Stops the server from listening on the socket.
- /// </summary>
- /// <returns>True if the socket has been successfully close; otherwise false.</returns>
- public bool StopListening()
- {
- lock (m_lock_listensocket)
- {
- if (Running)
- {
- // Surround the entire Stop routine with a try-catch block to prevent errors
- // from breaking the code.
- try
- {
- // First we stop the timer from executing. That way no additional requests
- // can/wil be processed.
- if (m_timer_listen != null)
- m_timer_listen.Dispose();
- // Now that the timer is no longer active, we can close the socket and stop
- // listening on it.
- m_socket.Close();
- // Return true since we succeeded in stopping the server.
- Running = false;
- return true;
- }
- catch (ApplicationException ex)
- {
- // A problem occured while trying to stop the server. Raise the OnErrorEvent if possible
- // and return false since the server could not be properly stopped.
- RaiseOnErrorEvent(ex);
- return false;
- }
- }
- // Return true here cause the server was not running.
- return true;
- }
- }
- #endregion
- #region Private Methods
- protected void Dispose(bool disposeManagedResources)
- {
- // Check if we're already disposed of. No need to do anything
- // if we're already disposed.
- if (!m_disposed)
- {
- // Check if we need to dispose of the managed resouces of our class.
- if (disposeManagedResources)
- {
- // First dispose of the internal timer.
- if (m_timer_listen != null)
- m_timer_listen.Dispose();
- // Unsubscribe all our eventhandlers.
- OnClientConnected -= m_event_connected;
- OnClientDisconnected -= m_event_disconnected;
- OnClientData -= m_event_data;
- OnError -= m_event_error;
- // Dispose of the Yarn.
- m_yarn.Dispose();
- // Close the socket.
- StopListening();
- }
- // Dispose all the unmanaged resources here.
- }
- // Set the disposed flag to true.
- m_disposed = true;
- }
- /// <summary>
- /// Listens for incoming connections on the server socket.
- /// When a connection is available, the server will create a new
- /// processing fiber for this socket and keep track of it through the
- /// internal list.
- /// This function is non-blocking and will be called periodicly from an
- /// internal timer on a seperate thread.
- /// </summary>
- /// <param name="state">The state of the timer calling the function. In this case null.</param>
- virtual protected void Listen(object state)
- {
- // Try to obtain the lock on the listenlock so we have
- // exclusive access to the socket. If the lock cannot
- // be obtained, simply fall through the function and
- // wait till the next call.
- if (Monitor.TryEnter(m_lock_listensocket))
- {
- // Use a try-finally statement to catch errors and always release the lock.
- try
- {
- // Check if there is a connection pending on our socket. If there
- // is a connection pending, accept the socket. If there are no
- // connections pending, simply fall through the function and wait
- // till the next call.
- if (m_socket.Poll(0, SelectMode.SelectRead))
- {
- // There is a connection pending, so accept the connection.
- // Create a new Fiber based upon the socket.
- IFiber f = new Fiber(new TCPClient(m_socket.Accept()));
- // Raise the onClientConnected event if possible.
- RaiseOnClientConnectedEvent(f.Client);
- // Check if the client is still connected. If the client is no longer
- // connected to the server, raise the on Disconnect event and exit
- // the function.
- if (!f.Client.Connected)
- {
- // No longer connected, so raise the onDisconnect event if possible.
- RaiseOnClientDisconnectedEvent(f.Client);
- // Get rid of the fiber we created.
- f.Dispose();
- // Exit the function.
- return;
- }
- // Configure the fiber to take over the events we require.
- f.OnData += m_event_data;
- // Add the fiber to the Yarn of fibers.
- Yarn.Add(f);
- // Start the fiber
- f.Start(0, 10);
- }
- }
- catch(ApplicationException ex)
- {
- // Raise the onError event if possible.
- RaiseOnErrorEvent(ex);
- }
- finally
- {
- // Release the lock we just acquired.
- Monitor.Exit(m_lock_listensocket);
- }
- }
- }
- /// <summary>
- /// Raises the OnError event asynchronously if there are subscribers to the event.
- /// If there are no subscribers to the event, the call is ignored.
- /// </summary>
- /// <param name="ex">The exception that has been raised.</param>
- virtual protected void RaiseOnErrorEvent(ApplicationException ex)
- {
- // Obtain the lock to the events so we have exclusive access
- // to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to a local variable to prevent racing conditions
- // from breaking the code.
- EventHandler<ErrorEventArgs> handler = m_event_error;
- // Check if there are sufficient subscribers.
- if (handler != null)
- handler.BeginInvoke(this, new ErrorEventArgs(ex), new AsyncCallback(CleanCallBack), null);
- }
- }
- /// <summary>
- /// Raises the OnClientConnected event synchronously in the code if there
- /// subscribers available.
- /// </summary>
- virtual protected void RaiseOnClientConnectedEvent(IClient client)
- {
- // Obtain the lock to the events so we have exclusive access
- // to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to a local variable to prevent racing conditions
- // from breaking the code.
- EventHandler handler = m_event_connected;
- // Check if the handler is not null, if it is not null it means there
- // are subscribers availalbe for the event.
- if (handler != null)
- handler(client, new EventArgs());
- }
- }
- /// <summary>
- /// Raises the OnClientDisconnected event synchronously in the code if there
- /// are subscribers available.
- /// </summary>
- /// <param name="client">The client that has disconnected.</param>
- virtual protected void RaiseOnClientDisconnectedEvent(IClient client)
- {
- // Obtain the lock to the event so we have exclusive access
- // to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to a local variable to prevent racing conditions
- // from breaking the code.
- EventHandler handler = m_event_disconnected;
- // Check if the handler is not null, if it is not null it means there are
- // subscribers to the event.
- if (handler != null)
- handler(client, new EventArgs());
- }
- }
- /// <summary>
- /// Cleans the result of an asynchronous callback and releases all the resources
- /// used by the delegate of the callback.
- /// </summary>
- /// <param name="result"></param>
- virtual protected void CleanCallBack(IAsyncResult result)
- {
- // Cast the result back to a concrete result type to get the runtime delegate.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
- // Clean the callback.
- (aresult.AsyncDelegate as EventHandler<ErrorEventArgs>).EndInvoke(result);
- }
- #endregion
- #region Private Fields
- private Socket m_socket; // The socket of the server.
- private Yarn m_yarn; // The yarn that keeps track of the IFibers.
- private readonly object m_lock_listensocket = new object(); // The lock to gain exclusive access to the socket.
- private readonly object m_lock_yarn = new object(); // The lock to gain exclusive access to the yarn.
- private readonly object m_lock_event = new object(); // The lock to gain exclusive access to the events.
- private bool m_disposed = false; // Value indicating if the object has been disposed.
- private int m_port; // The port used to listen for incoming connections.
- private int m_maxconnections; // The maximum number of connections allowed on the server.
- private int m_interval; // The interval in ms between listen operations.
- private bool m_running = false; // Value indicating whether the server is running or not.
- private Timer m_timer_listen; // Multithreaded timer that calls the listen routine.
- private EventHandler m_event_connected; // Event to raise when a client connects.
- private EventHandler m_event_disconnected; // Event to raise when a client disconnects.
- private EventHandler<ClientDataEventArgs> m_event_data; // Event to raise when a client sends data.
- private EventHandler<ErrorEventArgs> m_event_error; // Event to raise when an error occurs.
- #endregion
- }
- }
Expand|Select|Wrap|Line Numbers
- public class Fiber : IFiber
- {
- #region Constructor
- /// <summary>
- /// Creates a new instance of the TCPFiber class and assigns the values from the
- /// parameters to the internal fields of the class.
- /// </summary>
- /// <param name="client">The IClient wrapped by the Fiber.</param>
- public Fiber(IClient client)
- {
- // Save the values in the internal fields.
- Client = client;
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the IClient that is beeing wrapped & processed by the Fiber.
- /// </summary>
- public IClient Client
- {
- get { lock (m_lock_client) { return m_client; } }
- set { lock (m_lock_client) { m_client = value; } }
- }
- /// <summary>
- /// Gets a value indicating if the Fiber is processing incoming data.
- /// Readonly property.
- /// </summary>
- public bool Running
- {
- get { return m_running; }
- protected set { m_running = value; }
- }
- #endregion
- #region Public Events
- /// <summary>
- /// This event gets raised by the Fiber when there is data from the client.
- /// The event will contain the array of data, the client that raised the event
- /// and the size of the data recieved.
- /// </summary>
- public event EventHandler<ClientDataEventArgs> OnData
- {
- add { lock (m_lock_event) { m_event_ondata += value; } }
- remove { lock (m_lock_event) { m_event_ondata -= value; } }
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Disposes of the Fiber object and releases all managed and unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- // Dispose of the managed and unmanaged resources in our class.
- Dispose(true);
- // Suppress the finalization process of the GC to prevent undefined
- // behavior during the cleaning of memory.
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Starts the Fiber to process incoming data from the underlying IClient and raise the
- /// appropiate event every time there is data availalbe.
- /// </summary>
- /// <param name="delay">The amount of ms to wait before starting the first call.</param>
- /// <param name="interval">The amount of ms between each subsequent call.</param>
- public void Start(int delay, int interval)
- {
- // Create a new instance of the multithreaded timer to start the processing of
- // the incoming data on the socket of the client.
- m_processtimer = new Timer(new TimerCallback(ProcessRoutine), null, delay, interval);
- // Set the flag to true since the Fiber has been initiated.
- Running = true;
- }
- /// <summary>
- /// Stops the Fiber to process data from the underlying IClient object.
- /// </summary>
- public void Stop()
- {
- // Dispose of the timer, so we dont have to process further data requests.
- m_processtimer.Dispose();
- // Set the flag to false since the Fiber has been stopped.
- Running = false;
- }
- #endregion
- #region Private Methods
- /// <summary>
- /// This function gets called on a regular interval to process the underlying client for incoming
- /// data. If there is data to be read, the function will raise the OnData event.
- /// </summary>
- /// <param name="state">The state passed by the timer. Null in this case.</param>
- protected virtual void ProcessRoutine(object state)
- {
- // Try to obtain a lock on the client for exclusive access to the client.
- // If the lock cannot be obtained, fall through the function because it
- // means that another routine is still going.
- if (Monitor.TryEnter(m_lock_client))
- {
- // Now that we have the lock, we need to check if there is data
- // available on the client to read. Save this data in a seperate
- // variable because we will read a snapshot.
- int bytes_to_read = Client.DataAvailable;
- // If there is data available, perform a read operation on the IClient
- // and raise the event. If there is no data available to read, fall
- // through the function and try again on the next call.
- if (bytes_to_read > 0)
- {
- // Create a new buffer to hold the information. Perform the read
- // operation and use the return value of that operation to raise
- // the OnData event with the correct parameters.
- byte[] buffer = new byte[bytes_to_read];
- RaiseOnDataEvent(buffer, Client.Receive(buffer, 0, bytes_to_read));
- }
- }
- }
- /// <summary>
- /// Internal function that diposed of all the resources claimed by the class.
- /// Calling this function will completely destroy the object and release all
- /// the resources claimed by the class. All background threads and services
- /// are beeing halted as well.
- /// </summary>
- /// <param name="disposeManagedResources">Value indicating if the managed resources need to be cleaned as well.</param>
- protected virtual void Dispose(bool disposeManagedResources)
- {
- // Only perform the disposed operation if we're not disposed of
- // already. Otherwise we'll get reference errors.
- if (!m_disposed)
- {
- // Check if we need to clean up the resources that are beeing
- // managed by the GarbageCollector.
- if (disposeManagedResources)
- {
- // Dispose all the managed resources.
- m_processtimer.Dispose();
- m_client.Disconnect();
- m_client.Dispose();
- OnData -= m_event_ondata;
- }
- // Dispose of the unmanaged resources
- }
- // Set the disposed flag to true.
- m_disposed = true;
- }
- /// <summary>
- /// Raises the onData event of the Fiber with the supplied parameters.
- /// If there are subscribers to the event, the event will be raised, if
- /// there are no subscribers, the event is ignored by the function.
- /// </summary>
- /// <param name="data">The data read from the underlying IClient.</param>
- /// <param name="size">The size of the data read from the IClient.</param>
- private void RaiseOnDataEvent(byte[] data, int size)
- {
- // Obtain a lock on the event so we have short exclusive
- // access to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to a private variable to prevent racing
- // conditions between the raising and unsubscribing of the event.
- EventHandler<ClientDataEventArgs> handler = m_event_ondata;
- // Check if the handler is not null. If the handler is not null, it
- // means there are subscribers to the event and we can safely raise
- // the event.
- if (handler != null)
- handler(this, new ClientDataEventArgs(data, size));
- }
- }
- #endregion
- #region Private Fields
- private bool m_disposed = false; // Value indicating if the Fiber has been disposed of.
- private bool m_running = false; // Value indicating if the Fiber is processing incomming data.
- private readonly object m_lock_event = new object(); // Lock on the events of the Fiber.
- private readonly object m_lock_client = new object(); // Lock on the client of the Fiber.
- private EventHandler<ClientDataEventArgs> m_event_ondata; // Eventhandler to raise the onData event.
- private IClient m_client; // IClient object beeing wrapped & processed.
- private Timer m_processtimer; // Timer that fires the process routine on specified intervals.
- #endregion
- }
Expand|Select|Wrap|Line Numbers
- public class TCPClient : IClient
- {
- #region Constructor
- /// <summary>
- /// Creates a new instance of the TCPClient class and assigns the parameter values
- /// to the internal fields of the class.
- /// </summary>
- /// <param name="host">The remote host to establish a connection on.</param>
- /// <param name="port">The remote port to establsih a connection on.</param>
- public TCPClient(string host, int port)
- {
- // Assign the parameters to the fields.
- IP = host;
- Port = port;
- m_socket = null;
- }
- /// <summary>
- /// Creates a new instance of the TCPClient class and assigns the parameter values
- /// to the internal fields of the class.
- /// </summary>
- /// <param name="socket">The socket on which to base the connection.</param>
- public TCPClient(Socket socket)
- {
- // Assign the parameters to the fields.
- m_socket = socket;
- IP = (m_socket.RemoteEndPoint as IPEndPoint).Address.ToString();
- Port = (m_socket.RemoteEndPoint as IPEndPoint).Port;
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets a value indicating if the TCPClient is currently connected to a remote location.
- /// </summary>
- public bool Connected
- {
- get { return ((m_socket == null) ? false : m_socket.Connected); }
- }
- /// <summary>
- /// Gets or sets the IPAddress of the remote host to which a connections needs to be established.
- /// </summary>
- public string IP
- {
- get { return m_ip; }
- set { m_ip = value; }
- }
- /// <summary>
- /// Gets or sets the port of the remote host on which to establish a connection.
- /// </summary>
- public int Port
- {
- get { return m_port; }
- set { m_port = value; }
- }
- /// <summary>
- /// Gets the amount of data available on the internal socket to be read.
- /// Retuns -1 if the socket is not available.
- /// </summary>
- /// <exception cref="System.Net.Sockets.SocketException">A problem occured accessing the underlying socket.</exception>
- public int DataAvailable
- {
- get { return ((m_socket == null) ? -1 : m_socket.Available); }
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Establishes a connection to the remote host using the parameters of the class.
- /// If the connection is succesfully established, the function will return true and
- /// data can be received or transmitted.
- /// </summary>
- /// <returns>True if the connection attempt succeeds; otherwise false.</returns>
- public bool Connect()
- {
- // Obtain a lock on the underlying socket for exclusive access during the
- // entire connection attempt.
- lock (m_lock_socket)
- {
- // Check if the client is not connected already. We don't need to perform
- // a connection attempt if the client is already connected.
- 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 socket to connect to the remote
- // location.
- m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- m_socket.NoDelay = true;
- // Try to establish the connection to the remote location.
- m_socket.Connect(IP, Port);
- // If nothing went wrong, the function will return true at the end.
- RaiseOnConnectedEvent();
- }
- catch (ApplicationException ex)
- {
- // A problem occured while trying to establish the connection to the remote
- // host. Raise the onError event and exit the function.
- RaiseOnErrorEvent(ex);
- return false;
- }
- }
- // Return true since the client is connected.
- return true;
- }
- }
- /// <summary>
- /// Disconnects the TCPClient from the remote location and releases all internal resources
- /// from the underlying socket.
- /// </summary>
- /// <returns>True if the TCPClient is properly disconnected from the remote location; otherwise false.</returns>
- public bool Disconnect()
- {
- // Obtain a lock on the socket so we can perform the disconnect in a thread-safe environment.
- lock (m_lock_socket)
- {
- // Check if the client is actually connected. No need to perform the entire
- // disconnect attempt if the client is not connected.
- if (Connected)
- {
- // Surround the entire operation with a try-catch statement to prevent errors from
- // breaking the code.
- try
- {
- // Perform a shutdown first on the socket to prevent additional data to linger
- // in the buffers.
- m_socket.Shutdown(SocketShutdown.Both);
- // Close the socket.
- m_socket.Close();
- // If nothing went wrong the function will return true at the end.
- RaiseOnDisconnectedEvent();
- }
- catch (ApplicationException ex)
- {
- // An exception occured while performing the disconnect routine on the
- // socket. Raise the onError event.
- RaiseOnErrorEvent(ex);
- return false;
- }
- }
- // Return true regardless if we performed the disconnect or not.
- return true;
- }
- }
- /// <summary>
- /// Performs a read operation on the socket. If there is data available on the socket, the
- /// read operation will try to read out the specified block of data from the socket.
- /// </summary>
- /// <param name="buffer">The byte array to hold the data read from the socket.</param>
- /// <param name="offset">The offset in the byte array at which the filling starts.</param>
- /// <param name="length">The amount of bytes to read from the socket.</param>
- /// <returns>The amount of bytes read from the socket.</returns>
- public int Receive(byte[] buffer, int offset, int length)
- {
- // Obtain a lock on the socket to gain exclusive access to the socket.
- lock (m_lock_socket)
- {
- // Check if there is data available, If there is no data available,
- // exit the function.
- if (DataAvailable > 0)
- {
- // There is information available. Perform a read operation on the socket
- // and return the amount of bytes read.
- return m_socket.Receive(buffer, offset, length, SocketFlags.None);
- }
- else
- {
- // No data to read, so return 0.
- return 0;
- }
- }
- }
- /// <summary>
- /// Performs a send operation on the underlying socket to transmit data to the remote host.
- /// The function will try to send as much data as possible in a single go.
- /// </summary>
- /// <param name="data">the data as a byte array that needs to be sent.</param>
- /// <param name="offset">the offset in the data array at which the sending begins</param>
- /// <param name="length">The length of the data to send.</param>
- /// <returns>The amount of bytes that have been sent.</returns>
- public int Send(byte[] data, int offset, int length)
- {
- // Obtain a lock on the socket to gain exclusive access to the socket.
- lock (m_lock_socket)
- {
- // Surround the write operation with a try-catch statement to prevent
- // errors from breaking the code.
- try
- {
- // Try to send that data and return the amount of bytes
- // actually sent.
- return m_socket.Send(data, offset, length, SocketFlags.None);
- }
- catch (ApplicationException ex)
- {
- // An excepton occured while sending the data. Raise the event
- // and return 0.
- RaiseOnErrorEvent(ex);
- return 0;
- }
- }
- }
- /// <summary>
- /// Disposes the TCPClient and all the internal resources.
- /// </summary>
- public void Dispose()
- {
- // Dispose of ourselves including the managed resources.
- Dispose(true);
- // Suppress the GC.
- GC.SuppressFinalize(this);
- }
- #endregion
- #region Events
- /// <summary>
- /// This event gets fired when the Client successfully establishes a connection to the remote location.
- /// </summary>
- public event EventHandler OnConnect
- {
- add { lock (m_lock_event) { m_event_connect += value; } }
- remove { lock (m_lock_event) { m_event_connect -= value; } }
- }
- /// <summary>
- /// This event gets fired when the Client successfully disconnects from the remote location.
- /// </summary>
- public event EventHandler OnDisconnct
- {
- add { lock (m_lock_event) { m_event_disconnect += value; } }
- remove { lock (m_lock_event) { m_event_disconnect -= value; } }
- }
- /// <summary>
- /// This event gets fired when the client encounters an internal error.
- /// </summary>
- public event EventHandler<ErrorEventArgs> OnError
- {
- add { lock (m_lock_event) { m_event_error += value; } }
- remove { lock (m_lock_event) { m_event_error -= value; } }
- }
- #endregion
- #region Private Methods
- /// <summary>
- /// Raises the onError event asynchronously.
- /// </summary>
- /// <param name="ex">The Exception that triggered the event.</param>
- private void RaiseOnErrorEvent(ApplicationException ex)
- {
- // use the lock to gain exclusive access to the event handler.
- lock (m_lock_event)
- {
- // Copy the eventhandler to a local variable to prevent racing conditions.
- EventHandler<ErrorEventArgs> handler = m_event_error;
- // Raise the event asynchronously if possible.
- if (handler != null)
- handler.BeginInvoke(this, new ErrorEventArgs(ex), new AsyncCallback(CleanCallbackResult), null);
- }
- }
- /// <summary>
- /// Raises the onConnected event.
- /// </summary>
- private void RaiseOnConnectedEvent()
- {
- // Use the lock to gain exclusive access to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to prevent racing conditions.
- EventHandler handler = m_event_connect;
- // Raise the eventhandler if possible
- if (handler != null)
- handler(this, new EventArgs());
- }
- }
- /// <summary>
- /// Raises the onDisconnected event.
- /// </summary>
- private void RaiseOnDisconnectedEvent()
- {
- // Use the lock to gain exclusive access to the event handler.
- lock (m_lock_event)
- {
- // Copy the event handler to prevent racing conditions.
- EventHandler handler = m_event_disconnect;
- // Raise the eventhandler if possible
- if (handler != null)
- handler(this, new EventArgs());
- }
- }
- /// <summary>
- /// Cleans the result of the asynchronous callback event of our onError event.
- /// </summary>
- /// <param name="result">The result of the callback.</param>
- private void CleanCallbackResult(IAsyncResult result)
- {
- // Cast the received result to an AsyncResult so we can extract the delegate.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = result as System.Runtime.Remoting.Messaging.AsyncResult;
- // Extract the delegate and clean the callback.
- (aresult.AsyncDelegate as EventHandler<ErrorEventArgs>).EndInvoke(result);
- }
- /// <summary>
- /// Performs the actuall dispose request and releases all resources used
- /// by the managed and unmanaged parts of the code.
- /// </summary>
- /// <param name="disposeManagedResources">Value indicating to release managed resources as well.</param>
- protected void Dispose(bool disposeManagedResources)
- {
- // Check if we're already disposed. Nothing to do if we're disposed.
- if (!m_disposed)
- {
- // Check if we need to dispose the managed resources.
- if (disposeManagedResources)
- {
- // Dispose our events first.
- OnConnect -= m_event_connect;
- OnDisconnct -= m_event_disconnect;
- OnError -= m_event_error;
- // Dispose of the socket.
- Disconnect();
- }
- // Dispose of the unmanaged resources.
- }
- // Change the disposed flag.
- m_disposed = true;
- }
- #endregion
- #region Private Fields
- private string m_ip; // Remote host.
- private int m_port; // Remote port.
- private Socket m_socket; // The underlying socket.
- private EventHandler m_event_connect; // Event fired when the client connects.
- private EventHandler m_event_disconnect; // Event fired when the client disconnets.
- private EventHandler<ErrorEventArgs> m_event_error; // Event fired when an internal error occurs.
- private readonly object m_lock_event = new object(); // Lock for exclusive access to the events.
- private readonly object m_lock_socket = new object(); // Lock for exclusive access to the underlying socket.
- private bool m_disposed = false; // Value indicating if the object has been disposed.
- #endregion
- }
Expand|Select|Wrap|Line Numbers
- public class Yarn : IList<IFiber>, IDisposable
- {
- #region Constructor
- /// <summary>
- /// Creates a new instance of the Yarn class with the specified collection to use as
- /// internal storage mechanism.
- /// </summary>
- /// <param name="collection">The IList implementation to use as internal storage for the IFiber objects.</param>
- public Yarn(IList<IFiber> collection)
- {
- // Assign the collection to the field.
- m_fibers = collection;
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the IFiber at the specific index.
- /// </summary>
- /// <param name="index">The zero-based index of the IFiber.</param>
- /// <returns>The IFiber at the specified index.</returns>
- public IFiber this[int index]
- {
- get { lock (m_lock) { return m_fibers[index]; } }
- set { lock (m_lock) { m_fibers[index] = value; } }
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Disposes of the Yarn and all the internal objects.
- /// </summary>
- public void Dispose()
- {
- // Clean ourselves and all our internal objects.
- Dispose(true);
- // Supress the GC.
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Determines the index of a specific IFiber in the Yarn.
- /// </summary>
- /// <param name="item">The specific IFiber to locate in the Yarn.</param>
- /// <returns>The index of the specific IFiber.</returns>
- public int IndexOf(IFiber item)
- {
- lock (m_lock) { return m_fibers.IndexOf(item); }
- }
- /// <summary>
- /// Inserts an IFiber into the Yarn at the specific location.
- /// </summary>
- /// <param name="index">The location in the Yarn to insert the IFiber.</param>
- /// <param name="item">The IFiber to insert into the Yarn.</param>
- public void Insert(int index, IFiber item)
- {
- lock (m_lock) { m_fibers.Insert(index, item); }
- }
- /// <summary>
- /// Removes the IFiber at the specific index.
- /// </summary>
- /// <param name="index">The index at which to remove the IFiber.</param>
- public void RemoveAt(int index)
- {
- lock (m_lock) { m_fibers.RemoveAt(index); }
- }
- /// <summary>
- /// Adds the specific IFiber to the Yarn.
- /// </summary>
- /// <param name="item">The IFiber to add to the Yarn.</param>
- public void Add(IFiber item)
- {
- lock (m_lock) { m_fibers.Add(item); }
- }
- /// <summary>
- /// Removes all IFibers from the Yarn
- /// </summary>
- public void Clear()
- {
- lock (m_lock) { m_fibers.Clear(); }
- }
- /// <summary>
- /// Checks if a specific IFiber is part of the Yarn.
- /// </summary>
- /// <param name="item">The specific IFiber to look for.</param>
- /// <returns>True if the specific IFiber is found; otherwise false.</returns>
- public bool Contains(IFiber item)
- {
- lock (m_lock) { return m_fibers.Contains(item); }
- }
- /// <summary>
- /// Copies the entire Yarn into the specified array at the specified index.
- /// </summary>
- /// <param name="array">The array to copy the Yarn into.</param>
- /// <param name="arrayIndex">The index in the array at which the coying starts.</param>
- public void CopyTo(IFiber[] array, int arrayIndex)
- {
- lock (m_lock) { m_fibers.CopyTo(array, arrayIndex); }
- }
- /// <summary>
- /// Returns the number of IFibers in the Yarn.
- /// </summary>
- public int Count
- {
- get { lock (m_lock) { return m_fibers.Count; } }
- }
- /// <summary>
- /// Returns a value indicating if the Yarn is marked as read-only.
- /// </summary>
- public bool IsReadOnly
- {
- get { return false; }
- }
- /// <summary>
- /// Removes a specific IFiber from the Yarn.
- /// </summary>
- /// <param name="item">The specific IFiber to remove from the Yarn.</param>
- /// <returns>True if the IFiber got removed successfully; otherwise false.</returns>
- public bool Remove(IFiber item)
- {
- lock (m_lock) { return m_fibers.Remove(item); }
- }
- /// <summary>
- /// Returns the enumerator to browse through the Yarn.
- /// </summary>
- /// <returns>The enumerator to browse through the Yarn</returns>
- public IEnumerator<IFiber> GetEnumerator()
- {
- lock (m_lock) { return ((IEnumerator<IFiber>)m_fibers.GetEnumerator()); }
- }
- /// <summary>
- /// Returns the enumerator to browse through the Yarn.
- /// </summary>
- /// <returns>The enumerator to browse through the Yarn</returns>
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- lock (m_lock) { return m_fibers.GetEnumerator(); }
- }
- #endregion
- #region Private Methods
- /// <summary>
- /// Performs the disposing operation on the internal objects.
- /// </summary>
- /// <param name="disposeManagedResources">Value indicating if the managed resources need to be disposed off.</param>
- protected void Dispose(bool disposeManagedResources)
- {
- // Check if we're already disposed or still need to clean ourselves.
- if (!m_disposed)
- {
- // Check if we need to dispose our managed resources.
- if (disposeManagedResources)
- {
- // First kick all clients and dispose of them.
- foreach (IFiber f in m_fibers)
- f.Dispose();
- // After all are cleared, remove the list of entries.
- Clear();
- }
- // Dispose of unmagaged resources.
- }
- // Set our disposed flag to true.
- m_disposed = true;
- }
- #endregion
- #region Private Fields
- private IList<IFiber> m_fibers; // The internal list of fibers beeing handled by this yarn.
- private readonly object m_lock = new object(); // Object to lock upon for exlusive access to the Fibers.
- private bool m_disposed = false; // Value indicating if the object has been disposed of.
- #endregion
- }