这两天在写socket通信,也就是下面的东西,然后遇到了个问题,怎样才能优雅的关闭socket(正常关闭而不是发生异常导致的关闭)
直接说思路吧,后面再说我遇到的问题!
我们这里说的关闭是用户点击断开按钮后再关闭,如果是用户直接退出程序可以在退出之前调用和关闭按钮一样的方法,或则采用心跳检测判断用户是否在连接,不过这里不说怎么实现这个。
我们一般只说客户端断开连接,如果想要服务器断开和客户的连接也可以采用同样的方法,但是实际情况服务器是一直运行的,所以我们说客户端的就行。
思路:
1、我们在点击断开连接按钮时并不是直接关闭socket,而是先给服务器发送断开连接消息;
2、服务器收到该消息后再向客户端发送确认消息;
3、服务器发送完确认消息之后断开连接;
4、客户端收到确认消息后再断开连接;
5、为了能够再次连接,客户端还要在主程序中关闭socket。(为什么要再次关闭可以看我后面的问题)
其实看到上面的思路应该知道怎么做了,只是很多时候我们没有想到是这样,然后直接关闭socket,虽然可以关闭,但是那时因为发生了异常,你不关闭程序也会帮你关闭。至于为什么会异常,很简单,在接收线程里面是一个死循环,它一直在监听服务器有没有给它发消息,当你关闭socket的时候你无法进入这个循环去关闭它,但是会影响到它使它为空或者突然被关闭(正在监听突然被关闭是有异常的)。
下面以我的代码为例:
主要看三个类
接收线程:一直在监听,为了使接收和发送不相互影响必须使用线程
发送线程:这个其实和普通类一样,因为只有在有需要的时候才发送,这时调用该类里面的发送方法即可,如果是控制台的话那它就必须线程,否则不能使发送和接收分离
主类:用于创建连接和关闭连接
1、当我们点击关闭按钮时,只需要给服务器发送一个关闭消息:readyCloseLink(),它会调用发送线程的发送方法给服务器发送断开连接消息bye
/**
* @Name
* @Description TODO 准备关闭,在点击关闭后并没有实际关闭,
* 而是给服务器发送关闭消息,服务器收到后返回确认消息,同时服务器关闭该连接
* 客户端接收到确认后接收线程中关闭socket,最后在本类中再次关闭socket以保证可以再次连接
* (接收线程中关闭socket并不能关闭本类的socket,在接收线程中关闭是为了程序正常退出而不是因为异常退出)
* @param
* @Return void
* @Author KingSSM
* @Date 2019/12/5 9:22
*/
public void readyCloseLink(){
new SendThread().closeClientSocket("bye");
}
//真正断开连接,此类中的socket置空,发送线程的clientSocket已经关闭,此处关闭可以确保可以再次连接
public void closeLink() {
try {
if (socket != null) {
socket.close();
}
socket = null;
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("<====== 警告:已断开连接 ======>");
uiUpdate.receiveMessage("<====== 警告:已断开连接 ======>")