3 ms·
Between Windows and Unix/Linux, the general approach is portable at least. Across various Unix flowers, I'd expect some more portability. Can't say much more si
by jstimpfle 5y ago
Between Windows and Unix/Linux, the general approach is portable at least. Across various Unix flowers, I'd expect some more portability. Can't say much more since I've never tried to use the same code on multiple platforms.
I'd expect you can easily code one backend per supported platform since the backend specific code can start out (and most likely, stay) fairly minimal, like 100 lines or so.
> Using the raw BSD sockets APIs as a starting point for any portable application in 2021 is fucking insane
I started a Linux POSIX sockets "embedded" server project in 2019 using BSD sockets API (TCP) that is rock-solid even though it has some critical low-latency components in the data path (~10ms).
I also worked on a Windows GUI project in 2020 using WinSock2 (TCP). Then I did several experimental projects on Linux POSIX sockets in 2021, building reliable streams on top of UDP. The platform is not that important, I used non-blocking sockets and moved from recvmsg()/sendmsg() to recvmmsg()/sendmmsg() as an optimization, which is maybe 20 lines more code on the backend.
I wasted several months with the wrong approaches on Windows first. I used WinSock2 with IOCP (asynchronous completion ports) and tried to be super clever with multi-threaded designs (roughly thread-per-connection models) and lots of synchronization, even going into "Fiber" approaches with custom scheduling.
That's all wrong, and I/O is very simple. You place buffers at the connections, then you pump data to/from the buffers on a regular basis. You write plain, simple, procedural code, no threading or any other cleverness needed. All you have to do, just like with files or any other I/O, is get rid of the expectation that you can write "nice" non-blocking code in any way. You just don't do that, it won't work out (expect for scripts / batch programs).
I don't see a reason why the story with TLS should be any different (never tried though). It should just be a component that you put between the network buffers and your application code. Something arrives from the network, you shove it to the TLS module. Something arrives from the TLS module, you shove it to the network.
> No RAII, means resource leaks (in C++).
Don't worry - it's just the same as with file descriptors or most other resources. If you're declaring them inline in a stack, something is wrong. Usually there should be exactly one place in the codebase where you're creating / accepting sockets, and one place where you're closing them. There's really nothing to worry about. There's so much C++ RAII zealotry and resource leaking FUD in the wild, but with a systematic appraoch there's little that can go wrong, plus the code will be so much better structured for out.
- nly 5y ago> You write plain, simple, procedural code, no threading or any other cleverness needed Using sockets in a synchronous fashion is one way to block for an indefinite period of time. Once a TCP connection is established, there are failure modes where nothing will notify you that the connection has been lost until you try to write(), and even then after minutes in the worst case. Using sockets without timeouts is nuts. The BSD sockets API doesn't give you timeouts. >I wasted several months with the wrong approaches on Windows first. I used WinSock2 with IOCP (asynchronous completion ports) If you'd used Boost ASIO you'd have gotten Windows IOCP under the covers for free. I honestly don't see an argument here. Defaulting to these low level primitive APIs is an act of hubris. Boost has HTTP, TLS and Websockets as well, all under the same async I/o model. Even HTTP/2 is available under asio via nghttp2
- jstimpfle 5y ago> The BSD sockets API doesn't give you timeouts. Of course you can get timeouts (using select() or any other standard event notification mechanism), and most importantly you can easily get non-blocking socket reads/writes, I did just that. > If you'd used Boost ASIO you'd have gotten Windows IOCP under the covers for free. Well, I got Windows IOCP without the covers. Even better, since now I can integrate all IOCP parts in my application, and don't have to separate the ones that are covered (or might be? hard to see when covered, right?) by library A from those that are covered by library B. But I'd like to see first whether IOCP is strictly needed anyway, synchronous non-blocking reads/writes might give you more than enough performance for most cases. > Boost has HTTP, TLS and Websockets as well I don't use Boost on principle. Maybe some of these libraries are usable, but boost is a community of architecture astronauts. Another reason is that I avoid C++ if possible. > Defaulting to these low level primitive APIs is an act of hubris. BSD sockets is not low level, if anything it is too high-level. As said, it allows you to send and receive packets. What more could you want? Anything else is snakeoil. Update: Yep, this seems to be some overarchitected junk that leads to unmaintainable messes: https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/overview/core/basics.html https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/ov... The basic primitive, receiving new updates, is not readily available. Instead, you're encouraged to do callback handlers, leading to temporal coupling and ravioli code. All in the name of optimizing for short syntax in toy examples. Look, how much you can do in just 5 lines with automatically inferred types, and pray the RAII! (Nevermind that anything moderately complex will require twice the normal amount of code just to unwrap all the insanity).