How to write a TCP-Server in Xojo?

Have you ever written your own little/or large TCP Server in Xojo?
…then I need your help!

I have found a bug in the TCPSocket class that makes my/your/everybody’s server vulnerable to DOS attacks: Let’s say, you have written a simple TCP Server and someone connects to it with FIREFOX and presses and holds F5 (reload), then it is absolutely certain, that your server will hang with 100% CPU load after a couple of seconds.

I have described everything and I have written a minimal test server and submitted this issue under #75595.

Please verify this very critical bug on your machine and if you can reproduce this critical bug, please give my issue report a “+1”/thumbs up. Hopefully, the Xojo team will recognize the importance of this issue and fix it soon!

Do you know a way around this problem?

In my opinion, with this critical bug in place, it is not possible to publish any service written in Xojo on the network.

https://tracker.xojo.com/xojoinc/xojo/-/issues/75595

I can reproduce the hang on macOS, and while it’s hung the stack trace looks like this:


Call graph:
    2406 Thread_355433   DispatchQueue_1: com.apple.main-thread  (serial)
    + 2406 start  (in dyld) + 1903  [0x7ff80560841f]
    +   2406 main  (in socketdemo.debug) + 19  [0x10a893873]
    +     2406 _Main  (in socketdemo.debug) + 846  [0x10a89416e]  /#main:61
    +       2406 REALbasic._RuntimeRun  (in socketdemo.debug) + 19  [0x10a8564e3]
    +         2406 RuntimeRun  (in rbframework.dylib) + 53  [0x10b668662]
    +           2406 CallFunctionWithExceptionHandling(void (*)())  (in rbframework.dylib) + 134  [0x10b6d03f5]
    +             2406 ConsoleApplication._CallFunctionWithExceptionHandling%%o<ConsoleApplication>p  (in socketdemo.debug) + 181  [0x10a838295]
    +               2406 CallConsoleApplicationRunEventHelper()  (in rbframework.dylib) + 325  [0x10b5f5aef]
    +                 2406 App.Event_Run%i8%o<App>A1s  (in socketdemo.debug) + 1576  [0x10a88ab38]  /App:18
    +                   2406 ConsoleApplication.DoEvents%%o<ConsoleApplication>i8  (in socketdemo.debug) + 11  [0x10a8380ab]
    +                     2406 RuntimeDoEvents  (in rbframework.dylib) + 16  [0x10b6e3f27]
    +                       2406 ModalEvents(unsigned char)  (in rbframework.dylib) + 54  [0x10b6d04cb]
    +                         2406 ???  (in rbframework.dylib)  load address 0x10b426000 + 0x2aa533  [0x10b6d0533]
    +                           2406 DoNetIdle(unsigned char)  (in rbframework.dylib) + 94  [0x10b61ddb7]
    +                             2406 TCPSocketPosix::Poll()  (in rbframework.dylib) + 1114  [0x10b601ac8]
    +                               2406 TCPSocket::FireEvents()  (in rbframework.dylib) + 565  [0x10b63129b]
    +                                 2406 HTTPServerSocket.Event_DataAvailable%%o<HTTPServerSocket>  (in socketdemo.debug) + 8485  [0x10a88f6f5]  /HTTPServerSocket:38
    +                                   2406 HTTPServer.Event_EndOfRequest%%o<HTTPServer>  (in socketdemo.debug) + 1075  [0x10a889373]  /HTTPServer:21
    +                                     2406 TCPSocket.Flush%%o<TCPSocket>  (in socketdemo.debug) + 11  [0x10a828f9b]
    +                                       1007 TCPSocketFlush  (in rbframework.dylib) + 29  [0x10b61f73e]
    +                                       ! 1007 TCPSocketPosix::Poll()  (in rbframework.dylib) + 6,0,...  [0x10b601674,0x10b60166e,...]
    +                                       831 TCPSocketFlush  (in rbframework.dylib) + 49  [0x10b61f752]
    +                                       ! 831 TCPSocketPosix::BytesLeftToSend()  (in rbframework.dylib) + 0,39,...  [0x10b602352,0x10b602379,...]
    +                                       568 TCPSocketFlush  (in rbframework.dylib) + 38,23,...  [0x10b61f747,0x10b61f738,...]

If I had to guess, it looks like perhaps your code is calling .Flush() inside of a .Poll() event, and perhaps that’s causing trouble?

1 Like

Do you have a fix for this (except from removing the flush call)?

No, but I have written a HTTPServer in Xojo.
In fact, looking back at my code (which I wrote about 5 years ago) I found this:

// Warning : this seems to lock up the app, don't do it!  
// Flush()

So this may be a long-standing bug? I saw it in Xojo 2019 R1.1, and it seems the same in 2024 R1.

2 Likes

Thanks for sharing.

If you remove the parameter (1000) from DoEvents, than it seems to work as expected, at least on macOS. Can’t get beyond 1.5 % CPU usage while pressing F5 quickly.

While True
  DoEvents
Wend
2 Likes

Instead doing a Write, Flush and Close together, implement the SendComplete event. Then you can just Write and, when the data has been sent, Close the socket, without having to use Flush at all.

5 Likes

Anyway, William just fixed the issue and you will be able to Flush and Close, if you want, in 2024r2 :clap:

15 Likes