I've written my own class for the UDPClient that whats the UdpClient from .NET
Using an Interface I define my own functions and map them downwards.
The class works, tested with unittests etc, but there's something that's puzzeling me.
When I call connect, disconnect, connect, disconnect, the class does what it is supposed to do, but the socket that is created during the first connect, stays alive after the second connect. I've timed it with a watch and using netstat -a, the socket stays alive for up to 15 seconds.
Expand|Select|Wrap|Line Numbers
- /// <summary>
- /// This class is a direct implementation of the IClient interface and transforms the functionality
- /// of the interface into the UDP protocol.
- /// </summary>
- public class UDPClient : IClient
- {
- #region Constructor
- /// <summary>
- /// Creates a new instance of the UDPClient class and initializes the internal
- /// fields with the values of the parameters.
- /// </summary>
- /// <param name="address">The remote address to connect to.</param>
- /// <param name="port">The remote port to connect to.</param>
- public UDPClient(string address, int port)
- {
- Address = address;
- Port = port;
- m_socket = new UdpClient();
- Asynchronous = false;
- m_listen_timer = null;
- }
- #endregion
- #region Public Methods
- /// <summary>
- /// Establishes a connection to a remote location and initializes the internal
- /// components to process incomming data.
- /// </summary>
- /// <returns>True if the connection has been successfully established; otherwise false.</returns>
- public bool Connect()
- {
- // Surround the entire operation with a try-catch statement to prevent
- // errors from breaking the code.
- try
- {
- // Check if we are connected already, if we're already connected
- // don't do anything.
- if (!Connected)
- {
- // Call the connect method on our client object to set the default host
- // and initialize the underlying socket.
- m_socket.Client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- m_socket.Connect(new IPEndPoint(IPAddress.Parse(Address), Port));
- // Initialize the timer to process incoming data.
- m_listen_timer = new Timer(new TimerCallback(ListenRoutine), null, 0, 10);
- // Raise the onConnected event.
- RaiseOnConnectEvent(Address, Port);
- }
- // If no errors occured, return true.
- return true;
- }
- catch { return false; }
- }
- /// <summary>
- /// Terminates the connection with a remote location and uninitializes the interna
- /// components that process incoming data.
- /// </summary>
- /// <returns>True if the connection has been successfully terminated; otherwise false.</returns>
- public bool Disconnect()
- {
- // Surround the entire operation with a try-catch statement to prevent
- // errors from breaking the code.
- try
- {
- // Check if we are connected. If we're not connected, don't
- // do anything
- if (Connected)
- {
- // Dispose of the timer first.
- m_listen_timer.Dispose();
- // Disconnect the underlying socket, but do not destroy the
- // object itself.
- m_socket.Client.Shutdown(SocketShutdown.Both);
- m_socket.Client.Close(0);
- m_socket.Client.Dispose();
- m_socket.Client = null;
- // Raise the OnDisconnected event.
- RaiseOnDisconnectEvent(Address, Port);
- }
- // If no errors occured, return true.
- return true;
- }
- catch { return false; }
- }
- /// <summary>
- /// Sends the binary diagram over the socket to the remote location.
- /// </summary>
- /// <param name="data">The UDP diagram in binary form.</param>
- /// <param name="length">The amount of bytes to send.</param>
- /// <returns>The amount of bytes send.</returns>
- public int Send(byte[] data, int length)
- {
- // Surround the code with a try-catch statement to prevent
- // errors from breaking the code.
- try { return m_socket.Send(data, length); }
- catch { return 0; }
- }
- /// <summary>
- /// Disposes the client and releases all internal resources claimed by the class.
- /// After calling dispose, the instance of the class can no longer be used.
- /// </summary>
- public void Dispose()
- {
- // Call our internal dispose method with the true parameter, as we're calling the
- // direct dispose.
- Dispose(true);
- // Surpress the finalize routine from the GC because we did the cleaning ourselves.
- GC.SuppressFinalize(this);
- }
- #endregion
- #region Public Properties
- /// <summary>
- /// Gets or sets the address of the remote destination.
- /// </summary>
- public string Address
- {
- get { return m_address; }
- set { m_address = value; }
- }
- /// <summary>
- /// Gets or sets the port of the remote destination.
- /// </summary>
- public int Port
- {
- get { return m_port; }
- set { m_port = value; }
- }
- /// <summary>
- /// Gets a value indicating whether or not the component is connected.
- /// </summary>
- public bool Connected
- {
- get { try { return ((m_socket != null) && m_socket.Client.Connected); } catch { return false; } }
- }
- /// <summary>
- /// Gets or sets a value indicating whether the events are fired asynchronous or not.
- /// </summary>
- public bool Asynchronous
- {
- get { return m_asynchronous; }
- set { m_asynchronous = value; }
- }
- #endregion
- #region Events
- /// <summary>
- /// This event gets raised whenever a new datagram is received.
- /// </summary>
- public event EventHandler<EventArgs.DiagramArguments> OnData
- {
- add { m_handler_data += value; }
- remove { m_handler_data -= value; }
- }
- /// <summary>
- /// This event gets raised when the UDPClient establishes a connection to a remote location.
- /// </summary>
- public event EventHandler<EventArgs.ConnectionArguments> OnConnect
- {
- add { m_handler_connect += value; }
- remove { m_handler_connect -= value; }
- }
- /// <summary>
- /// This event gets raised when the UDPClient disconnects from a remote location.
- /// </summary>
- public event EventHandler<EventArgs.ConnectionArguments> OnDisconnect
- {
- add { m_handler_disconnect += value; }
- remove { m_handler_disconnect -= value; }
- }
- #endregion
- #region Private Methods
- /// <summary>
- /// Disposes of the managed and unmanaged resources in the class. Whether or not the managed
- /// resources are beeing disposed of, depends on the value of the parameter.
- /// </summary>
- /// <param name="managed">Value indicating whether or not we dispose the managed resources.</param>
- void Dispose(bool managed)
- {
- // Check if we're not already disposed. If we're already diposed, simply fall through
- // the entire function.
- if (!m_disposed)
- {
- // Check if we need to cleanup our managed resources.
- if (managed)
- {
- // Unsubscribe the eventhandlers.
- OnConnect -= m_handler_connect;
- OnDisconnect -= m_handler_disconnect;
- OnData -= m_handler_data;
- // Get rid of the timers if they haven't already
- if (m_listen_timer != null) m_listen_timer.Dispose();
- // Dispose of our socket if we can. Surround the dispose method
- // with a try-catch because we don't want to raise an exception
- // in the finalizer.
- if (m_socket != null)
- {
- try
- {
- m_socket.Close();
- m_socket = null;
- }
- catch {/*empty catch block */}
- }
- }
- // Set our disposed flag to true;
- m_disposed = true;
- }
- }
- /// <summary>
- /// This function gets called on regular intervals by an internal timer and polls the socket
- /// for data. If there are UDP Diagrams available on the socket, the function will retrieve them
- /// all from the socket and raise the onData event for each Diagram read from the socket.
- /// </summary>
- /// <param name="state">the state of the timer calling the function.</param>
- void ListenRoutine(object state)
- {
- // Try to claim the lock for the listen routine. If the lock for the listenroutine cannot be
- // claimed, fall through the function and try again on the next attempt.
- if (Monitor.TryEnter(m_listen_lock))
- {
- // Because we have claimed a lock, we must also release this lock. To ensure we automaticly
- // release the lock when we are done, and to intercept errors, we will use a try-catch-finally
- // block around the entire code.
- try
- {
- // Check if the client has been initialized and is connected to a remote location.
- // Also validate if there is information on the socket to read.
- if ((m_socket != null) && Connected && (m_socket.Available > 0))
- {
- // There is data available on the socket to read for the application.
- // Because UDP transmits everything into defined datagrams, we can read
- // once to retrieve the entire datagram.
- // Perform the read operation on the socket and get the information from the socket.
- // Keep reading for as long as there is data available to read, this way we can flush
- // the entire socket in a single function call.
- while (m_socket.Available > 0)
- {
- // Prepare an endpoint reference to obtain the information about the connection.
- IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
- // Perform the read operation.
- byte[] diagram = m_socket.Receive(ref endpoint);
- // Invoke the OnData event for we have received data from the socket.
- RaiseOnDataEvent(ref diagram, endpoint.Address.ToString(), endpoint.Port);
- }
- }
- }
- catch {/*empty catch block*/}
- finally
- {
- // Always release the lock we have claimed, regardless of what has happened.
- Monitor.Exit(m_listen_lock);
- }
- }
- }
- /// <summary>
- /// Raises the onData event of the class. The event can be raised synchronously or
- /// asynchronously from the calling code depending on the settings of the class.
- /// </summary>
- /// <param name="diagram">The UDP diagram read from the socket.</param>
- /// <param name="ip">The remote ip address the diagram was send from.</param>
- /// <param name="port">The remote port the diagram was send from.</param>
- void RaiseOnDataEvent(ref byte[] diagram, string ip, int port)
- {
- // Copy the event handler to a local variable first. This prevents racing conditions
- // between the invocation and unsubscribing of an event.
- EventHandler<EventArgs.DiagramArguments> handle = m_handler_data;
- // Check if there eventhandlers assigned to the event. if there are no
- // eventhandlers assigned, exit the function
- if (handle != null)
- {
- // There's functions assigned to the event. Request the invocation list so
- // we can invoke each delegate seperatly.
- Delegate[] handlers = m_handler_connect.GetInvocationList();
- // Loop over all the handlers inside the array and invoke each handler
- // individually depending on the settings.
- foreach (Delegate pointer in handlers)
- {
- // Cast the handler to the correct type first.
- EventHandler<EventArgs.DiagramArguments> event_handle = (EventHandler<EventArgs.DiagramArguments>)pointer;
- // Surround the invoke with a try-catch to prevent errors.
- // Invoke the handle.
- try
- {
- if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.DiagramArguments(diagram, diagram.Length, new EventArgs.ConnectionArguments(ip, port, this)), new AsyncCallback(CleanConnectionCallback), null);
- else event_handle(this, new EventArgs.DiagramArguments(diagram, diagram.Length, new EventArgs.ConnectionArguments(ip, port, this)));
- }
- catch {/*empty catch block*/}
- }
- }
- }
- /// <summary>
- /// Raises the OnConnect event of the class. The event can be raised synchronously or
- /// asyncrhonously from the calling code depending on the settings of the class.
- /// </summary>
- /// <param name="ip">The remote ip of the established connection.</param>
- /// <param name="port">The remote port of the established connection.</param>
- void RaiseOnConnectEvent(string ip, int port)
- {
- // Copy the event handler to a local variable first. This prevents racing conditions
- // between the invocation and unsubscribing of an event.
- EventHandler<EventArgs.ConnectionArguments> handle = m_handler_connect;
- // Check if there eventhandlers assigned to the event. if there are no
- // eventhandlers assigned, exit the function
- if(handle != null)
- {
- // There's functions assigned to the event. Request the invocation list so
- // we can invoke each delegate seperatly.
- Delegate[] handlers = m_handler_connect.GetInvocationList();
- // Loop over all the handlers inside the array and invoke each handler
- // individually depending on the settings.
- foreach (Delegate pointer in handlers)
- {
- // Cast the handler to the correct type first.
- EventHandler<EventArgs.ConnectionArguments> event_handle = (EventHandler<EventArgs.ConnectionArguments>)pointer;
- // Surround the invoke with a try-catch to prevent errors.
- // Invoke the handle.
- try
- {
- if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.ConnectionArguments(ip, port, this), new AsyncCallback(CleanConnectionCallback), null);
- else event_handle(this, new EventArgs.ConnectionArguments(ip, port, this));
- }
- catch {/*empty catch block*/}
- }
- }
- }
- /// <summary>
- /// Raises the OnDisconnect event of the class. The event can be raised synchronously or
- /// asynchronously from the calling code depending on the settings of the class.
- /// </summary>
- /// <param name="ip">The remote ip of the terminated connection.</param>
- /// <param name="port">The remote port of the terminated connection.</param>
- void RaiseOnDisconnectEvent(string ip, int port)
- {
- // Copy the event handler to a local variable first. This prevents racing conditions
- // between the invocation and unsubscribing of an event.
- EventHandler<EventArgs.ConnectionArguments> handle = m_handler_disconnect;
- // Check if there eventhandlers assigned to the event. if there are no
- // eventhandlers assigned, exit the function
- if (handle != null)
- {
- // There's functions assigned to the event. Request the invocation list so
- // we can invoke each delegate seperatly.
- Delegate[] handlers = m_handler_connect.GetInvocationList();
- // Loop over all the handlers inside the array and invoke each handler
- // individually depending on the settings.
- foreach (Delegate pointer in handlers)
- {
- // Cast the handler to the correct type first.
- EventHandler<EventArgs.ConnectionArguments> event_handle = (EventHandler<EventArgs.ConnectionArguments>)pointer;
- // Surround the invoke with a try-catch to prevent errors.
- // Invoke the handle.
- try
- {
- if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.ConnectionArguments(ip, port, this), new AsyncCallback(CleanConnectionCallback), null);
- else event_handle(this, new EventArgs.ConnectionArguments(ip, port, this));
- }
- catch {/*empty catch block*/}
- }
- }
- }
- /// <summary>
- /// Cleans the resources claimed by the asynchronous invoke of the OnData event.
- /// </summary>
- /// <param name="result">The IAsyncResult of the asynchronous invoke.</param>
- void CleanDiagramCallback(IAsyncResult result)
- {
- // Cast the IAsyncResult to concrete implementation so we can access the delegate.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = (System.Runtime.Remoting.Messaging.AsyncResult)result;
- // Surround the cleaning with a try-catch to prevent errors.
- // Access the delegate, cast it and clean it.
- try { (aresult.AsyncDelegate as EventHandler<EventArgs.DiagramArguments>).EndInvoke(result); }
- catch {/*empty catch block*/}
- }
- /// <summary>
- /// Cleans the resources claimed by the asynchronous invoke of the OnConnect & OnDisconnect events.
- /// </summary>
- /// <param name="result">The IAsyncResult of the asynchronous invoke.</param>
- void CleanConnectionCallback(IAsyncResult result)
- {
- // Cast the IAsyncResult to concrete implementation so we can access the delegate.
- System.Runtime.Remoting.Messaging.AsyncResult aresult = (System.Runtime.Remoting.Messaging.AsyncResult)result;
- // Surround the cleaning with a try-catch to prevent errors.
- // Access the delegate, cast it and clean it.
- try { (aresult.AsyncDelegate as EventHandler<EventArgs.ConnectionArguments>).EndInvoke(result); }
- catch {/*empty catch block*/}
- }
- #endregion
- #region Private Fields
- EventHandler<EventArgs.DiagramArguments> m_handler_data; // The event handler to raise the OnData event.
- EventHandler<EventArgs.ConnectionArguments> m_handler_connect; // The event handler to raise the OnConnect event.
- EventHandler<EventArgs.ConnectionArguments> m_handler_disconnect; // The event handler to raise the OnDisconnect event.
- UdpClient m_socket; // The wrapped udp socket.
- Timer m_listen_timer; // Timer that polls for incomming data.
- bool m_asynchronous; // Value indicating whether the events are raised asynchronously or not.
- bool m_disposed = false; // Value indicating whether the instance is disposed or not.
- readonly object m_listen_lock = new object(); // Lock for exclusive access to the polling routine.
- int m_port; // The remote port to establish a connection on.
- string m_address; // The remote ip address to establish a connection on.
- #endregion
- }
Is this a coding problem, or is windows just beeing windows and is the data from netstat not correct...