Just thought that I'd throw something else out there: traditional networking education has been disastrous for programming large scale projects. For example, notice that nearly all web browsers still block and spin the beachball while loading a page.
Um, no, this is not true, and I can't think of any real web browser it's ever been true for. (Even NCSA Mosaic stayed responsive while it was loading a page, and demonstrated this via the 'throbber' animation.) In particular, every browser since Netscape 0.9 has loaded multiple resources in parallel, to speed up page loads, which you obviously can't do using simple-minded single-threaded blocking I/O.
I'm drawing a blank thinking of modern non-browser apps that beachball while reading from the network...
It's possible that something may occur while loading a page that takes an unexpectedly long time on the main thread and causes a brief beachball. This is possible if the system is under memory pressure and has to page stuff in. In the Safari app there is also some file I/O that takes place on the main thread, including periodic web-cache cleanups, that I've seen beachball if the disk is very busy. But none of that has to do with networking.
It's easier to begin programming with sync blocking, but that leads down the path that Java has chosen, basically bloated and complex projects riddled with race conditions and other hazards.
Amen! The downside of Java's making it so easy to use threads, is that everyone started using threads. Having had to rewrite Apple's AWT implementation at one point, I fervently agree that writing code that has to be called from any number of threads is very, very difficult.
Today's apps can't have even a single bug or they are considered unusable.
If only it were so … any shipping app has dozens of 'em. I've never worked on a release of any project that didn't ship with significant numbers of known bugs, and didn't have further bug reports come in later. (Who was it that said that any program longer than 100 lines contains bugs? (Unless it's TeX, of course. But we can't all be Knuth.))
After coming full circle, I have to say that my favorite way to handle multiple event streams is to use an event-based system similar to NSStream and runloops. Then, I use cooperative threads to avoid the use of state machines.
Yup, this is conceptually very much like the Actor model of concurrency that's used in a number of languages like Erlang and Scala. (Erlang is widely used for implementing vastly concurrent systems like telecom switches. Scala is a newish functional language that runs in the JVM; companies like Twitter are starting to use it for complex networking tasks.)
one can step through each phase of setup and teardown in a series of simple yielding wait loops in a cooperative thread. This is also known as a coroutine, something that has fallen out of favor but IMHO needs to be brought back and taught in schools.
Coroutines are part of some widely used languages (like Lua, and in a limited way in Python) but they're IMHO too limited as-is. They're often used as the building block for cooperative-threading APIs, though.
I also wish we had a construct for in-process processes. In other words, we need threads that don't have global variables, so that all state is passed through messages.