Bug? UDPSocket.Close throws Java Exception

dilettante

Active Member
Licensed User
Longtime User
See my source code attached in SNTPClient Class thread. Module SNTPClient.bas has comments containing @@@ you can do a find on.

I wanted/meant to call Close after using a UDPSocket object, assuming I might later call Initialize to make subsequent use of the same instance. This doesn't seem to work as I expected, though I may have been using it incorrectly or at the wrong time (i.e. from inside the PacketArrival event handler).

As a workaround the code discards instances after use, creating new instances as required.

To test the issue, you'd have to uncomment one line of code marked with @@@ in the PacketArrival handler Sub and then move the declaration of the instance of SNTPClient in the caller/parent to the module level instead of a local declaration.
 

agraham

Expert
Licensed User
Longtime User
I looked at this out of interest.

Firstly If usckSNTP.IsInitialized Then usckSNTP.Close at line 99 in SNTPClient.Query never executes the Close because IsInitialized returns False. This is because you call Dim SNTPClient As SNTPClient in Main.btnRequestTime_Action which recreates an SNTPClient instance. In fact you don't need to Close it to re-initialize it as Initialize calls Close on itself anyway.

HoweverI think there may be a slight problem with Close anyway. If you call SNTPClient.Close or usckSNTP.Initialize at the top of SNTPClient.tmrBlockingInterval_Tick it throws that "socket closed" exception. That classes as what I would call "unexpected behaviour", even though the exception seems to be benign. It might be worth a look by Erel, if he has the time!
 

dilettante

Active Member
Licensed User
Longtime User
Yes, the code as uploaded does create a new SNTPClient instance each time. That works out ok for a low volume operation like making an SNTP time query.

But there is other code there, most of it commented out, that is from my first attempt where I declared a module-global instance and reused it.

I agree that the test-and-close line of code does nothing and can be removed, being merely left over from some experimentation. I plan to re-post a cleaned up version once I know what is going on with UDPSocket.Close, because people tend to take examples and copy/paste them into future applications.


What it seems to boil down to is that UDPSocket.Close always throws an exception. Calling Close would seem to be the obvious way to free resources when you no longer need them, to be followed by a new Initialize call later on (possibly using a different remote host, etc.). Discarding the old instance and later creating a new instance works, but that seems like overkill and extra overhead for most applications.

So I'm wondering whether this is true and a bug, or a usage error on my part.
 

agraham

Expert
Licensed User
Longtime User
Seems to be straightforward, Dim it, open it, wait a bit for the threads to start, close it -
Program started.
java.net.SocketException: socket closed
at java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:121)
at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:145)
at java.net.DatagramSocket.receive(DatagramSocket.java:786)
at anywheresoftware.b4a.objects.SocketWrapper$UDPSocket$UDPReader.run(SocketWrapper.java:382)
at java.lang.Thread.run(Thread.java:744)
I think it is because when you close the DatagramSocket while UDPReader is in the blocking receive call it throws a "socket closed" exception and not the InterruptedException you are trapping.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
http://translate.google.com/?hl=en&tab=wT#auto/en/benign
I now see ;)

I will update the code to prevent the log in this case.

It is printed here:
B4X:
    public void run() {
         while (working) {
           try {
             DatagramPacket p = new DatagramPacket(new byte[receiveLength], receiveLength);
             socket.receive(p);
             UDPPacket u = new UDPPacket();
             u.setObject(new MyDatagramPacket("", 0, p));
             ba.raiseEventFromDifferentThread(null, null, 0, eventName + "_packetarrived", false, new Object[] {u});
           } catch (IOException e) {
             e.printStackTrace(); <-----------------
             if (working) {
               try {
                 Thread.sleep(100);
               } catch (InterruptedException e1) {
               }
             }
           }
         }
       }
 
Top