на клиенте можно отследить только целевое закрытие программы (нажатие соответствующей кнопки), для нецелевого завершения клиентского приложения можно нагородить костыль в виде дополнительной программы демона, который запускается при запуске основной программы, фиксирует PID основного приложения и по кд пингует его, когда PID пропал или присвоился другому отправлять сообщение на сервер, но это костыль и не спасет от ситуации закрытия всех программ через диспетчер задач, и даже если закрытие приложение удалось поймать на клиенте - как отправиь эту инфу на сервер если, допустим, на клиенте уже нет интернета
на сервер условное сообщение "disconnected"
просто на сервере отслеживать когда коннект оборвался, это может быть предусмотренное событие разрыва соединения, можно просто пинг (от сервера) понг (от клиента) реализовать, когда пинг не проходит - считаем, что приложение закрыли. тут может быть коллизия с тем, что приложение запущено, но отключился интернет например