Unless a FIN or RST is sent, the only other way standard TCP knows if the other side has quit, or gone away, is an expiring timeout.
This is really the best you can do by design if you are making the least amount of assumptions of the "other side" as possible, and your only connection to this "other side" is a single network interface.
An event lower on the OSI model may cause the OS to terminate the connection on its own, or provide information that could tell the client to pretty much just give up, such as removal of the network adapter in question, or the link state changing to being disconnected. This doesn't have to happen, though.
The general solution is to send periodic messages with no meaningful data, called a keepalive, through the connection. TCP supports this, but you can do it on the application layer as well.
- FileZilla can be set to send keepalive messages. You can see it send commands that don't do anything useful, but are just issued to throw something down the pipe and keep it "busy."
- PuTTY, for example, has keepalive options as well.
- You can also set keepalive options on the server as well, but it's usually better to set that on the SSH client.
- Many routing protocols send keepalives on the application layer to verify other routes are working and available, BGP, for one.
- TCP also supports keepalive messages natively as well, they are basically null Ethernet packets sent every so often to verify the connection. This tldp.org article explains well.