Writing a Simple Network Client
A server that finishes one client before accepting the next, as the DateServer sample does, can be held up by a single slow or idle connection.
One Slow Customer, Everyone Waits
Imagine a small shop with one counter and one clerk. The clerk serves a customer from start to finish before turning to the next person in line. If the first customer is quick, nobody minds. If the first customer settles in and talks for an hour, everyone behind them waits for that entire hour. A network server that handles connections one at a time has exactly this problem. This article looks at how a server can be structured so that one lingering client does not stop everyone else.
What do you think happens?
In the shop, the first customer talks for an hour. What happens to the customer standing behind them?
Reveal answer
They wait for the entire hour before being served
The clerk handles one customer from start to finish before turning to the next person in line, so the customer behind the talkative one waits the whole time.
How a Non-Threaded Server Takes Turns
The DateServer.java sample program from this course is an extremely simple network server, and it does not use threads. Because of that, the server has to finish its work with one client before it can accept a connection from a different client. The server is not doing two jobs at once. It works through its connections in order.
The exchange with a client might be very brief, and for a simple date service it often is. Even so, we cannot assume every client will behave that way. A client could connect, stay connected for a long time, and keep the server occupied the whole while. In a server with no threads, every client that arrives during that time has to wait. The timeline below traces this for two clients.
Read the timeline from left to right. Client 2 has already tried to connect, but the server is still occupied with Client 1, so Client 2 cannot be served. The server is not slow at any single task; the problem is that there is only one line of work, and the first client controls how long that line stays busy.
Tracing an Idle Client Step by Step
The source names a specific problem case. A misbehaving client is one that stays connected without sending the data the server expects. This is not a client that sends bad data. It is a client that sends nothing useful while keeping the connection open. From the server's point of view, it is waiting for input that never arrives. The sequence below follows that situation in order.
The key point in the sequence is the server's single line of work. While the server is waiting on Client A for data that is not coming, it has no free capacity to deal with Client B. Client B's connection request exists, but the server, which finishes one client before accepting the next, cannot move on until Client A is handled.
Giving Each Connection Its Own Thread
A threaded server changes the ordering problem. Instead of one path of execution that must finish with each client in turn, the server uses threads so that it deals with several clients at the same time. A thread is an independent path of execution, so while one thread is waiting on one client, another thread can be working with a different client. The server is no longer locked to a single line.
The diagram shows only that several conversations can be in progress together, each on its own thread. It does not say the server is always fast, and it does not say threads are free. The benefit is that the progress of one client does not dictate when another client can be served. When Client A is slow or idle, Thread 2 can keep serving Client B.
Converting a server to use threads changes the server's structure, but the client program can usually stay the same. The same techniques apply to more complicated servers, so the idea learned on a simple server carries over.
Comparing the Serial and Threaded Designs
It helps to set the two designs next to each other. The non-threaded server in the DateServer example works through its clients in sequence. The threaded server gives separate threads to separate connections. The table summarizes the differences that matter for the problem studied here.
| Question | Serial server (no threads) | Threaded server (one thread per connection) |
|---|---|---|
| How clients are handled | One at a time, in order | Several at the same time, each on its own thread |
| Effect of an idle client | Blocks every client behind it | Ties up only its own thread; others continue |
| Waiting client's experience | Waits until the current client finishes | Served while other threads are busy with other clients |
| Structure of the server | Single path of execution | Several paths of execution |
Serial and threaded designs compared for the idle-client problem
When a Client Stays Silent
Watch what happens to the thread paired with a misbehaving client. That thread is waiting on a connection that is not producing the input it needs. In the source's words, this can hang up that thread indefinitely. The important point is what does not happen: the other threads keep going, and they can continue with other clients.
Working Through a Stalled Queue
A serial server is handling Client 1, which connected and then sent nothing. Client 2 and Client 3 are both trying to connect. Explain what Client 2 and Client 3 experience, and then explain how the outcome would change if the server gave each connection its own thread.
Hints
- Ask whether the server can accept Client 2 while it is still working with Client 1.
- Consider which thread is affected when Client 1 stays silent, and whether the other threads can still serve Client 2 and Client 3.
Mistakes When Reasoning About Threads
Common mistake Thinking a silent client is a client that sends bad data
What it looks likeA client connects and sends nothing while keeping the connection open.
Why it failsThe source describes a misbehaving client as one that stays connected without sending the data the server expects. The problem is the absence of input, not corrupted input.
Do this insteadDescribe the case as a connection that stays open without useful data.
Common mistake Assuming threads make each individual client faster
What it looks likeExpecting a single slow client to finish sooner just because the server has threads.
Why it failsThe source's point is that other clients need not wait; it does not claim each conversation becomes faster or that threads are free.
Do this insteadCredit threads with letting several clients progress together, not with speeding up any one of them.
Common mistake Believing the client program must be rewritten when the server gains threads
Why it failsThe source says converting the server changes its structure, and the client program can usually stay the same.
Do this insteadFocus structural changes on the server side.
Common mistake Assuming a non-threaded server can accept a new client while one is still connected
The source also notes that converting DateServer to use threads is not very useful in practice for such a simple server. DateServer's interaction with each client is short, so the benefit of threads hardly shows. The techniques matter most when individual interactions are long or unpredictable.
Key Takeaways
- A server that finishes one client before accepting the next, as DateServer does, can be held up by a single slow or idle connection.
- Threads are independent paths of execution, so a server with a thread per connection can serve several clients at the same time.
- A client that stays connected without sending the expected data can hang up its own thread indefinitely, but other threads can keep serving other clients.
- Converting a server to use threads changes the server's structure, while the client program can usually stay the same.