473,385 Members | 1,645 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Socket stays alive

Airslash
221 100+
Hello,

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
  1.     /// <summary>
  2.     /// This class is a direct implementation of the IClient interface and transforms the functionality
  3.     /// of the interface into the UDP protocol.
  4.     /// </summary>
  5.     public class UDPClient : IClient
  6.     {
  7.         #region Constructor
  8.  
  9.         /// <summary>
  10.         /// Creates a new instance of the UDPClient class and initializes the internal
  11.         /// fields with the values of the parameters.
  12.         /// </summary>
  13.         /// <param name="address">The remote address to connect to.</param>
  14.         /// <param name="port">The remote port to connect to.</param>
  15.         public UDPClient(string address, int port)
  16.         {
  17.             Address = address;
  18.             Port = port;
  19.             m_socket = new UdpClient();
  20.             Asynchronous = false;
  21.             m_listen_timer = null;
  22.         }
  23.  
  24.         #endregion
  25.  
  26.         #region Public Methods
  27.  
  28.         /// <summary>
  29.         /// Establishes a connection to a remote location and initializes the internal
  30.         /// components to process incomming data.
  31.         /// </summary>
  32.         /// <returns>True if the connection has been successfully established; otherwise false.</returns>
  33.         public bool Connect()
  34.         {
  35.             // Surround the entire operation with a try-catch statement to prevent 
  36.             // errors from breaking the code.
  37.             try
  38.             { 
  39.                 // Check if we are connected already, if we're already connected
  40.                 // don't do anything.
  41.                 if (!Connected)
  42.                 {
  43.                     // Call the connect method on our client object to set the default host
  44.                     // and initialize the underlying socket.                    
  45.                     m_socket.Client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  46.                     m_socket.Connect(new IPEndPoint(IPAddress.Parse(Address), Port));
  47.  
  48.                     // Initialize the timer to process incoming data.
  49.                     m_listen_timer = new Timer(new TimerCallback(ListenRoutine), null, 0, 10);
  50.  
  51.                     // Raise the onConnected event.
  52.                     RaiseOnConnectEvent(Address, Port);
  53.                 }
  54.  
  55.                 // If no errors occured, return true.
  56.                 return true;
  57.             }
  58.             catch { return false; }
  59.         }
  60.  
  61.         /// <summary>
  62.         /// Terminates the connection with a remote location and uninitializes the interna
  63.         /// components that process incoming data.
  64.         /// </summary>
  65.         /// <returns>True if the connection has been successfully terminated; otherwise false.</returns>
  66.         public bool Disconnect()
  67.         {
  68.             // Surround the entire operation with a try-catch statement to prevent
  69.             // errors from breaking the code.
  70.             try
  71.             {
  72.                 // Check if we are connected. If we're not connected, don't 
  73.                 // do anything
  74.                 if (Connected)
  75.                 {
  76.                     // Dispose of the timer first.
  77.                     m_listen_timer.Dispose();
  78.  
  79.                     // Disconnect the underlying socket, but do not destroy the
  80.                     // object itself.
  81.                     m_socket.Client.Shutdown(SocketShutdown.Both);
  82.                     m_socket.Client.Close(0);
  83.                     m_socket.Client.Dispose();
  84.                     m_socket.Client = null;
  85.  
  86.                     // Raise the OnDisconnected event.
  87.                     RaiseOnDisconnectEvent(Address, Port);
  88.                 }
  89.  
  90.                 // If no errors occured, return true.
  91.                 return true;
  92.             }
  93.             catch { return false; }
  94.         }
  95.  
  96.         /// <summary>
  97.         /// Sends the binary diagram over the socket to the remote location.
  98.         /// </summary>
  99.         /// <param name="data">The UDP diagram in binary form.</param>
  100.         /// <param name="length">The amount of bytes to send.</param>
  101.         /// <returns>The amount of bytes send.</returns>
  102.         public int Send(byte[] data, int length)
  103.         {
  104.             // Surround the code with a try-catch statement to prevent
  105.             // errors from breaking the code.
  106.             try { return m_socket.Send(data, length); }
  107.             catch { return 0; }
  108.         }
  109.  
  110.         /// <summary>
  111.         /// Disposes the client and releases all internal resources claimed by the class.
  112.         /// After calling dispose, the instance of the class can no longer be used.
  113.         /// </summary>
  114.         public void Dispose()
  115.         {
  116.             // Call our internal dispose method with the true parameter, as we're calling the
  117.             // direct dispose.
  118.             Dispose(true);
  119.  
  120.             // Surpress the finalize routine from the GC because we did the cleaning ourselves.
  121.             GC.SuppressFinalize(this);
  122.         }
  123.  
  124.         #endregion
  125.  
  126.         #region Public Properties
  127.  
  128.         /// <summary>
  129.         /// Gets or sets the address of the remote destination.
  130.         /// </summary>
  131.         public string Address
  132.         {
  133.             get { return m_address; }
  134.             set { m_address = value; }
  135.         }
  136.  
  137.         /// <summary>
  138.         /// Gets or sets the port of the remote destination.
  139.         /// </summary>
  140.         public int Port
  141.         {
  142.             get { return m_port; }
  143.             set { m_port = value; }
  144.         }
  145.  
  146.         /// <summary>
  147.         /// Gets a value indicating whether or not the component is connected.
  148.         /// </summary>
  149.         public bool Connected
  150.         {
  151.             get { try { return ((m_socket != null) && m_socket.Client.Connected); } catch { return false; } }
  152.         }
  153.  
  154.         /// <summary>
  155.         /// Gets or sets a value indicating whether the events are fired asynchronous or not.
  156.         /// </summary>
  157.         public bool Asynchronous
  158.         {
  159.             get { return m_asynchronous; }
  160.             set { m_asynchronous = value; }
  161.         }
  162.  
  163.         #endregion
  164.  
  165.         #region Events
  166.  
  167.         /// <summary>
  168.         /// This event gets raised whenever a new datagram is received.
  169.         /// </summary>
  170.         public event EventHandler<EventArgs.DiagramArguments> OnData
  171.         {
  172.             add { m_handler_data += value; }
  173.             remove { m_handler_data -= value; }
  174.         }
  175.  
  176.         /// <summary>
  177.         /// This event gets raised when the UDPClient establishes a connection to a remote location.
  178.         /// </summary>
  179.         public event EventHandler<EventArgs.ConnectionArguments> OnConnect
  180.         {
  181.             add { m_handler_connect += value; }
  182.             remove { m_handler_connect -= value; }
  183.         }
  184.  
  185.         /// <summary>
  186.         /// This event gets raised when the UDPClient disconnects from a remote location.
  187.         /// </summary>
  188.         public event EventHandler<EventArgs.ConnectionArguments> OnDisconnect
  189.         {
  190.             add { m_handler_disconnect += value; }
  191.             remove { m_handler_disconnect -= value; }
  192.         }
  193.  
  194.         #endregion
  195.  
  196.         #region Private Methods
  197.  
  198.         /// <summary>
  199.         /// Disposes of the managed and unmanaged resources in the class. Whether or not the managed
  200.         /// resources are beeing disposed of, depends on the value of the parameter.
  201.         /// </summary>
  202.         /// <param name="managed">Value indicating whether or not we dispose the managed resources.</param>
  203.         void Dispose(bool managed)
  204.         {
  205.             // Check if we're not already disposed. If we're already diposed, simply fall through
  206.             // the entire function.
  207.             if (!m_disposed)
  208.             {
  209.                 // Check if we need to cleanup our managed resources.
  210.                 if (managed)
  211.                 {
  212.                     // Unsubscribe the eventhandlers.
  213.                     OnConnect -= m_handler_connect;
  214.                     OnDisconnect -= m_handler_disconnect;
  215.                     OnData -= m_handler_data;
  216.  
  217.                     // Get rid of the timers if they haven't already
  218.                     if (m_listen_timer != null) m_listen_timer.Dispose();
  219.  
  220.                     // Dispose of our socket if we can. Surround the dispose method
  221.                     // with a try-catch because we don't want to raise an exception
  222.                     // in the finalizer.
  223.                     if (m_socket != null)
  224.                     {
  225.                         try
  226.                         {
  227.                             m_socket.Close();
  228.                             m_socket = null;
  229.                         }
  230.                         catch {/*empty catch block */}
  231.                     }
  232.                 }
  233.  
  234.                 // Set our disposed flag to true;
  235.                 m_disposed = true;
  236.             }
  237.         }
  238.  
  239.         /// <summary>
  240.         /// This function gets called on regular intervals by an internal timer and polls the socket
  241.         /// for data. If there are UDP Diagrams available on the socket, the function will retrieve them
  242.         /// all from the socket and raise the onData event for each Diagram read from the socket.
  243.         /// </summary>
  244.         /// <param name="state">the state of the timer calling the function.</param>
  245.         void ListenRoutine(object state)
  246.         {
  247.             // Try to claim the lock for the listen routine. If the lock for the listenroutine cannot be 
  248.             // claimed, fall through the function and try again on the next attempt.
  249.             if (Monitor.TryEnter(m_listen_lock))
  250.             {
  251.                 // Because we have claimed a lock, we must also release this lock. To ensure we automaticly
  252.                 // release the lock when we are done, and to intercept errors, we will use a try-catch-finally
  253.                 // block around the entire code.
  254.                 try 
  255.                 {
  256.                     // Check if the client has been initialized and is connected to a remote location.
  257.                     // Also validate if there is information on the socket to read.
  258.                     if ((m_socket != null) && Connected && (m_socket.Available > 0))
  259.                     {
  260.                         // There is data available on the socket to read for the application.
  261.                         // Because UDP transmits everything into defined datagrams, we can read
  262.                         // once to retrieve the entire datagram.                        
  263.  
  264.                         // Perform the read operation on the socket and get the information from the socket.
  265.                         // Keep reading for as long as there is data available to read, this way we can flush
  266.                         // the entire socket in a single function call.
  267.                         while (m_socket.Available > 0)
  268.                         {
  269.                             // Prepare an endpoint reference to obtain the information about the connection.
  270.                             IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
  271.  
  272.                             // Perform the read operation.
  273.                             byte[] diagram = m_socket.Receive(ref endpoint);
  274.  
  275.                             // Invoke the OnData event for we have received data from the socket.
  276.                             RaiseOnDataEvent(ref diagram, endpoint.Address.ToString(), endpoint.Port);
  277.                         }
  278.                     }
  279.                 }
  280.                 catch {/*empty catch block*/}
  281.                 finally
  282.                 {
  283.                     // Always release the lock we have claimed, regardless of what has happened.
  284.                     Monitor.Exit(m_listen_lock);
  285.                 }
  286.             }
  287.         }
  288.  
  289.         /// <summary>
  290.         /// Raises the onData event of the class. The event can be raised synchronously or
  291.         /// asynchronously from the calling code depending on the settings of the class.
  292.         /// </summary>
  293.         /// <param name="diagram">The UDP diagram read from the socket.</param>
  294.         /// <param name="ip">The remote ip address the diagram was send from.</param>
  295.         /// <param name="port">The remote port the diagram was send from.</param>
  296.         void RaiseOnDataEvent(ref byte[] diagram, string ip, int port)
  297.         {
  298.             // Copy the event handler to a local variable first. This prevents racing conditions
  299.             // between the invocation and unsubscribing of an event.
  300.             EventHandler<EventArgs.DiagramArguments> handle = m_handler_data;
  301.  
  302.             // Check if there eventhandlers assigned to the event. if there are no
  303.             // eventhandlers assigned, exit the function
  304.             if (handle != null)
  305.             {
  306.                 // There's functions assigned to the event. Request the invocation list so
  307.                 // we can invoke each delegate seperatly.
  308.                 Delegate[] handlers = m_handler_connect.GetInvocationList();
  309.  
  310.                 // Loop over all the handlers inside the array and invoke each handler
  311.                 // individually depending on the settings.
  312.                 foreach (Delegate pointer in handlers)
  313.                 {
  314.                     // Cast the handler to the correct type first.
  315.                     EventHandler<EventArgs.DiagramArguments> event_handle = (EventHandler<EventArgs.DiagramArguments>)pointer;
  316.  
  317.                     // Surround the invoke with a try-catch to prevent errors.
  318.                     // Invoke the handle.
  319.                     try
  320.                     {
  321.                         if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.DiagramArguments(diagram, diagram.Length, new EventArgs.ConnectionArguments(ip, port, this)), new AsyncCallback(CleanConnectionCallback), null);
  322.                         else event_handle(this, new EventArgs.DiagramArguments(diagram, diagram.Length, new EventArgs.ConnectionArguments(ip, port, this)));
  323.                     }
  324.                     catch {/*empty catch block*/}
  325.                 }
  326.             }
  327.         }
  328.  
  329.         /// <summary>
  330.         /// Raises the OnConnect event of the class. The event can be raised synchronously or
  331.         /// asyncrhonously from the calling code depending on the settings of the class.
  332.         /// </summary>
  333.         /// <param name="ip">The remote ip of the established connection.</param>
  334.         /// <param name="port">The remote port of the established connection.</param>
  335.         void RaiseOnConnectEvent(string ip, int port)
  336.         {   
  337.             // Copy the event handler to a local variable first. This prevents racing conditions
  338.             // between the invocation and unsubscribing of an event.
  339.             EventHandler<EventArgs.ConnectionArguments> handle = m_handler_connect;
  340.  
  341.             // Check if there eventhandlers assigned to the event. if there are no
  342.             // eventhandlers assigned, exit the function
  343.             if(handle != null)
  344.             {
  345.                 // There's functions assigned to the event. Request the invocation list so
  346.                 // we can invoke each delegate seperatly.
  347.                 Delegate[] handlers = m_handler_connect.GetInvocationList();
  348.  
  349.                 // Loop over all the handlers inside the array and invoke each handler
  350.                 // individually depending on the settings.
  351.                 foreach (Delegate pointer in handlers)
  352.                 {
  353.                     // Cast the handler to the correct type first.
  354.                     EventHandler<EventArgs.ConnectionArguments> event_handle = (EventHandler<EventArgs.ConnectionArguments>)pointer;
  355.  
  356.                     // Surround the invoke with a try-catch to prevent errors.
  357.                     // Invoke the handle.
  358.                     try
  359.                     {
  360.                         if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.ConnectionArguments(ip, port, this), new AsyncCallback(CleanConnectionCallback), null);
  361.                         else event_handle(this, new EventArgs.ConnectionArguments(ip, port, this));
  362.                     }
  363.                     catch {/*empty catch block*/}
  364.                 }
  365.             }
  366.         }
  367.  
  368.         /// <summary>
  369.         /// Raises the OnDisconnect event of the class. The event can be raised synchronously or
  370.         /// asynchronously from the calling code depending on the settings of the class.
  371.         /// </summary>
  372.         /// <param name="ip">The remote ip of the terminated connection.</param>
  373.         /// <param name="port">The remote port of the terminated connection.</param>
  374.         void RaiseOnDisconnectEvent(string ip, int port)
  375.         {
  376.             // Copy the event handler to a local variable first. This prevents racing conditions
  377.             // between the invocation and unsubscribing of an event.
  378.             EventHandler<EventArgs.ConnectionArguments> handle = m_handler_disconnect;
  379.  
  380.             // Check if there eventhandlers assigned to the event. if there are no
  381.             // eventhandlers assigned, exit the function
  382.             if (handle != null)
  383.             {
  384.                 // There's functions assigned to the event. Request the invocation list so
  385.                 // we can invoke each delegate seperatly.
  386.                 Delegate[] handlers = m_handler_connect.GetInvocationList();
  387.  
  388.                 // Loop over all the handlers inside the array and invoke each handler
  389.                 // individually depending on the settings.
  390.                 foreach (Delegate pointer in handlers)
  391.                 {
  392.                     // Cast the handler to the correct type first.
  393.                     EventHandler<EventArgs.ConnectionArguments> event_handle = (EventHandler<EventArgs.ConnectionArguments>)pointer;
  394.  
  395.                     // Surround the invoke with a try-catch to prevent errors.
  396.                     // Invoke the handle.
  397.                     try
  398.                     {
  399.                         if (Asynchronous) event_handle.BeginInvoke(this, new EventArgs.ConnectionArguments(ip, port, this), new AsyncCallback(CleanConnectionCallback), null);
  400.                         else event_handle(this, new EventArgs.ConnectionArguments(ip, port, this));
  401.                     }
  402.                     catch {/*empty catch block*/}
  403.                 }
  404.             }
  405.         }
  406.  
  407.         /// <summary>
  408.         /// Cleans the resources claimed by the asynchronous invoke of the OnData event.
  409.         /// </summary>
  410.         /// <param name="result">The IAsyncResult of the asynchronous invoke.</param>
  411.         void CleanDiagramCallback(IAsyncResult result)
  412.         { 
  413.             // Cast the IAsyncResult to concrete implementation so we can access the delegate.
  414.             System.Runtime.Remoting.Messaging.AsyncResult aresult = (System.Runtime.Remoting.Messaging.AsyncResult)result;
  415.  
  416.             // Surround the cleaning with a try-catch to prevent errors.
  417.             // Access the delegate, cast it and clean it.
  418.             try { (aresult.AsyncDelegate as EventHandler<EventArgs.DiagramArguments>).EndInvoke(result); }
  419.             catch {/*empty catch block*/}
  420.         }
  421.  
  422.         /// <summary>
  423.         /// Cleans the resources claimed by the asynchronous invoke of the OnConnect & OnDisconnect events.
  424.         /// </summary>
  425.         /// <param name="result">The IAsyncResult of the asynchronous invoke.</param>
  426.         void CleanConnectionCallback(IAsyncResult result)
  427.         {
  428.             // Cast the IAsyncResult to concrete implementation so we can access the delegate.
  429.             System.Runtime.Remoting.Messaging.AsyncResult aresult = (System.Runtime.Remoting.Messaging.AsyncResult)result;
  430.  
  431.             // Surround the cleaning with a try-catch to prevent errors.
  432.             // Access the delegate, cast it and clean it.
  433.             try { (aresult.AsyncDelegate as EventHandler<EventArgs.ConnectionArguments>).EndInvoke(result); }
  434.             catch {/*empty catch block*/}
  435.         }
  436.  
  437.         #endregion
  438.  
  439.         #region Private Fields
  440.  
  441.         EventHandler<EventArgs.DiagramArguments> m_handler_data;            // The event handler to raise the OnData event.
  442.         EventHandler<EventArgs.ConnectionArguments> m_handler_connect;      // The event handler to raise the OnConnect event.
  443.         EventHandler<EventArgs.ConnectionArguments> m_handler_disconnect;   // The event handler to raise the OnDisconnect event.
  444.         UdpClient m_socket;                                                 // The wrapped udp socket.
  445.         Timer m_listen_timer;                                               // Timer that polls for incomming data.
  446.  
  447.         bool m_asynchronous;                                                // Value indicating whether the events are raised asynchronously or not.
  448.         bool m_disposed = false;                                            // Value indicating whether the instance is disposed or not.
  449.         readonly object m_listen_lock = new object();                       // Lock for exclusive access to the polling routine.
  450.         int m_port;                                                         // The remote port to establish a connection on.
  451.         string m_address;                                                   // The remote ip address to establish a connection on.
  452.  
  453.         #endregion
  454.     }
  455.  
I've tried several things in the Disconnect method to get rid of the socket, but it stays alive too long to my liking. When I call disconnect on my class I want the object to be gone, killed, deleted, removed from existance....

Is this a coding problem, or is windows just beeing windows and is the data from netstat not correct...
Sep 16 '10 #1
2 3182
Plater
7,872 Expert 4TB
Pain in the butt isn't it? I know this is the way it works with TCP connections, I guess UDP would be the same way.(Also I think it stays for about 30seconds)

I first started looking this up when I had a piece of testing software open/close a connection a few thousand times. It started telling me I had run out of port numbers to use.
From what I read it is part of the networking stack. That timeout is like hardcoded.
I ended up setting some socket options to tell it to shutdown without waiting for loose data and to mark the socket address as reusable. Do not think they helped any, but I felt better.
Sep 16 '10 #2
Airslash
221 100+
guess we're two of the same kind :)

I hate it when a network/components decides on it's own what it should do.I'm the programmer here, I bloody decide how a component should work/act...

The component currently does what it's supposed to do and passed all unit tests with a big green V mark, so I'm happy. It's troublesome that the socket behaves this way, but for now I can live with it.
Sep 16 '10 #3

Sign in to post your reply or Sign up for a free account.

Similar topics

2
by: Anand Pillai | last post by:
I would like to add a feature to one of my network programs. The requirement is to find out if a server is alive by sending it requests. I know of the standard ICMP ping, and searched for python...
3
by: Thomas Hervé | last post by:
My problem is not really python specific but as I do my implementation in python I hope someone here can help me. I have two programs that talk through a socket. Here is the code : <server>...
4
by: Scott Robinson | last post by:
I have been having trouble with the garbage collector and sockets. Unfortunately, google keeps telling me that the problem is the garbage collector ignoring dead (closed?) sockets instead of...
6
by: Anders Both | last post by:
If you send data using TCP and Socket, can the client then be 100% sure that if it send´s data and no exception uccur, then the data will also arrived in a a correct way on the server. What if...
1
by: aplaton101 | last post by:
What is the best way to create a TCP Server which receives connections from several sockets and stores information that will be used for the next connection from each of the sockets. How can I...
6
by: Luis P. Mendes | last post by:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, I've developed a program that uses a socket to receive information 24h a ~ day. The problem is that the socket seems to freeze. By that I...
15
by: nephish | last post by:
hey there, i have a script that waits for message packets from a data server over a socket. it goes a little like this: while 1: x+=1 databack = sockobj.recv(158) if databack:
4
by: TheGTA | last post by:
Hi there! I am working on a project for like 1 year now. Most of it works well. But somehow my socketenvironment craps out sometimes... For a totally unknown reason a socket crashes with...
0
by: od88 | last post by:
edit: nevermind fixed it by re-opening the sockets whenever this one specific error string is detected this is my first time using winsock in years and am having some trouble. pretty sure all it...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.