It’s important to remember that sockets in general have a buffer for incoming and outgoing data. That is, for incoming data, socket data is queued into a buffer before the DataAvailable event fires and is held there until you actually read from the socket. This is why you can use lookahead and then read it later.
On an outgoing buffer, any time that you write data it is also put into a buffer and fed to the client at a rate that it can handle which also doesn’t bog down other sockets and threads. Calling Flush may make a single socket perform better, but it also pauses the rest of the app while doing so and the performance for the rest of the users will suffer.
Remember, sockets all run their code on the main thread, regardless of where you write to them, so they’re all sharing the main thread’s time. That main thread is also responsible for keeping everything else working cooperatively, including the app itself.
So anyway yes, you might get individual sockets to get 100Mbps locally, but once you have multiple sockets running, you’ll end up being limited by the slowest connection the way you’re doing it. Allowing the socket to send data off as it sees fit will get you much better concurrent performance overall.
…at least that’s what we found when updating the web framework for web 2.