B4J Question Issue with jWebSocketClient v2.5 closing client after connection lost to server

Chris2

Well-Known Member
Licensed User
Longtime User
I think there may be an issue within jWebSocketClient v2.5 (jetty 12.1 based);
when the client applicaiton is closed after the connection to the server is lost - the client application process keeps running.

I've attached test apps and an SQLite database, and would be grateful if someone can give this a go to see if I'm missing something or if there is indeed something amiss in jWebSocketClient v2.5. Thanks!

To recreate the problem:
1. Open the jRDC2SQLite server app and adjust the database path in RDCConnector.Initialize to suit where you put the database (attached in SQLIteTestDB.zip)
2. Run the server in Debug mode.
3. Run the client app (attached in BasicJRDC2ClientTest.zip), in Release mode.
4. Click the 'Connect Websocket' button

At this point if you check what java apps are running (using ...\java\bin\jps -l) you'll get something like:
D:\Java\jdk-17.0.16\bin>jps -l
16976 b4j.example.main
22676 jdk.jcmd/sun.tools.jps.Jps
9480 b4j.example.main
17308 D:\B4J\JServer5Tests\Basic\Client\Objects\BasicJRDC2ClientTest.jar

5. Stop the jRDC server app (immitating a server end connection issue).
D:\Java\jdk-17.0.16\bin>jps -l
5204 jdk.jcmd/sun.tools.jps.Jps
17308 D:\B4J\JServer5Tests\Basic\Client\Objects\BasicJRDC2ClientTest.jar

6. Close the client app (just close the form). The client app process remains:
D:\Java\jdk-17.0.16\bin>jps -l
10388 jdk.jcmd/sun.tools.jps.Jps
17308 D:\B4J\JServer5Tests\Basic\Client\Objects\BasicJRDC2ClientTest.jar

If I run the same tests using jWebSocketClient 2.1, then the client process closes as Id expect. It's just when using the new jWebSocketClient 2.5 the process keeps running.
I thought it might be something to do with the two timers in WSPushService but disabling them in WSPushService.Close makes no difference.

I've shared a video of this issue here:

Many thanks!
 

Attachments

  • jRDCSQlite.zip
    16.7 KB · Views: 12
  • SQLiteTestDB.zip
    554 bytes · Views: 10
  • BasicJRDC2ClientTest.zip
    9.1 KB · Views: 13

teddybear

Well-Known Member
Licensed User
You can add ExitApplication in sub MainForm_Closed for ClientTest.
B4X:
Private Sub MainForm_Closed
    If WSPushService.IsConnected Then
        WSPushService.Close
    End If
    ExitApplication
End Sub
 
Last edited:
Upvote 0

teddybear

Well-Known Member
Licensed User
ExitApplication will work, though there might be something to fix here.
The "something" that needs fixing here is not the jWebSocketClient library. it is the BasicJRDC2ClientTest app.
This is a bug in class module WSPushService. ‌It should come from this example.
Comment out the definition of this local variable in wsh Sub Connect .This is what causes the WebSocket to fail to close.
B4X:
Private Sub Connect
    Log("wspushservice.connect")
'    Dim wsh As WebSocketHandler
    If Not(wsh.IsInitialized) Then wsh.Initialize(Me, "wsh")
    If Not(wsh.ws.Connected) Then wsh.Connect2($"wss://localhost:17178/push"$, Main.RDCUser, Main.RDCPW)
'    If Not(wsh.ws.Connected) Then wsh.Connect2($"ws://localhost:17177/push"$, Main.RDCUser, Main.RDCPW)
End Sub
 

Attachments

  • BasicJRDC2ClientTest.zip
    9.1 KB · Views: 7
Last edited:
Upvote 0

Chris2

Well-Known Member
Licensed User
Longtime User
You need to add ExitApplication in sub MainForm_Closed for ClientTest
That solves the problem, thanks. But I didn't think that ExitApplication was neccesary in UI apps, and it wasn't needed before the new jWebSocketClient v2.5.

I've also noted that I can solve the problem by removing the 'ws.Connected' checks in WSPushService.Close and WebSocketHandler.Close, so making sure that WebSocketClient.Close definitely gets called at app close solves this particular problem too.

Having said that, there's clearly still something different in the new jWebSocketClient v2.5, because none of this was necessary with v2.1.

Can you post the thread dump generated with jstack: https://docs.oracle.com/en/java/javase/17/docs/specs/man/jstack.html ?
After you closed the connection.
See attached. I wasn't sure exactly when to run jStack, so jStack-AfterServerKill.txt is fro after I killed the server with the client still running, and jStack-AfterAppFormClose.txt is from after I closed the client form (but the process was still running.

Not sure if it's relevant, but I did also note in the client logs a slight difference between jWebSocketClient 2.1 vs 2.5. When the server is killed, the client app using v2.1 reports an extra msg:
2026-09-08 14:22:08.242:INFO :eek:ejwc.WebSocketClient:pool-3-thread-1: Shutdown WebSocketClient@b5d3f0eb[coreClient=WebSocketCoreClient@6473dd10{STARTED},openSessions.size=1]
This msg doesn;t appear when using v2.5.

Thanks both!
 

Attachments

  • jStack-AfterAppFormClose.txt
    21 KB · Views: 7
  • jStack-AfterServerKill.txt
    80.8 KB · Views: 8
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
The "something" that needs fixing here is not the jWebSocketClient library. it is the BasicJRDC2ClientTest app.
This is a bug in class module WSPushService. ‌It should come from this example.
I agree. The declaration of wsh inside Connect sub, will cause the previous instance to remain in an unknown state. I guess that I wrote it as a "brute force" solution for a new connection to be made.

I would start with something like:
B4X:
'PushService
Private Sub Connect
    If Initialized(wsh) Then wsh.Close
    Dim wsh As WebSocketHandler
    wsh.Initialize(Me, "wsh")
    wsh.Connect(Main.serverLink)
End Sub

And Sub Close in WebSocketHandler:
B4X:
Public Sub Close
    ws.Close
End Sub
 
Upvote 0

Chris2

Well-Known Member
Licensed User
Longtime User
The code was certainly based on the PushService example, albeit a long time ago.
I would start with something like:
B4X:
'PushService
Private Sub Connect
    If Initialized(wsh) Then wsh.Close
    Dim wsh As WebSocketHandler
    wsh.Initialize(Me, "wsh")
    wsh.Connect(Main.serverLink)
End Sub
So you'd keep the declaration of wsh inside Connect sub as well as the global one?
But make sure that any existing instance is closed first?
 
Upvote 0
Top