Что же такого понаписать в catch (IOException){...}для try {socket.close()}, чтобы гарантировать закрытие здесь и сейчас.
Ничего. Как вы и сказали, 1 пункт можно обработать. 2 пункт тоже можно. Можно перехватить InterruptedIOException, сбросить interrupted флаг потока, повторно попробовать закрыть сокет и в конце восстановить interrupted флаг. Но при правильно спроектированном приложении такая ситуация невозможна. Так что я бы не стал добавлять проверку на такой случай. Если вдруг она возникнет, значит нужно пересмотреть архитектуру приложения, а не обвешиваться дополнительными проверками. Это, кстати, так же относится и к 1 случаю. Если сокет закрывается дважды, значит код кривой и его надо переделать.
В остальных случаях из java вы ничего не сможете сделать, так как должно что-то очень серьёзно сломаться, чтобы close начал стрелять исключениями. Максимум, что вы можете сделать - отпустить ссылку на ваш экземпляр ServerSocket, чтобы GC при сборке мог попробовать с этим что-то сделать.
Также, в спецификации написано про закрытие сокета (ServerSocket) через PrintWrite.close() или BufferedReader.close(), если мы их инициализировали потоками ServerSocket, но они также выбрасывают IOException. Как же быть?
Эти два класса - просто обёртки, которые тупо вызывают close у потока, который они оборачивают. Так что поведение их метода close в точности соответствует поведению обёрнутого ими стрима.