Not that hard, really. Common practice is to keep a counter of active I/O requests. During shutdown, set a flag to prevent new requests from starting, and cancel active requests still in progress. When requests complete, decrement the counter. Wait for the counter to fall to 0, then close the socket and exit.
Probably not the best thing to do in a destructor in a GUI, though. I would have the GUI start the shutdown process and let it run asynchronously, then disable the UI and tell the user to wait, and then finish cleaning up the GUI only when all pending requests have finished.