ServerSocket连接断开处理方式

    • 1、概述:
    • 2、异常信息:
      • 2.1、之前有人给出的方案:
    • 3、代码分析
    • 4、场景分析
      • 4.1.建立Socket连接,底层就是TCP连接:
      • 4.2.发送数据
      • 4.3.断开连接
    • 5、总结:

1、概述:

WebSocket是一种在单个TCP连接上进行全双工通信的协议。在我们应用的过程仲,客户端会出现无故断开的情况。这里提供一种连接断开的异常检测机制。

2、异常信息:

系统中出现的异常
2019-03-12 18:56:24,044 ERROR [com.lenovo.SocketServer.ping(172)] -
2019-03-12 18:56:24,045 ERROR [com.lenovo.SocketServer.ping(173)] - <java.SocketException: Software caused connection abort: recv failed>

2.1、之前有人给出的方案:

总结产生原因,在服务端/客户端单方面关闭连接的情况下,另一方依然以为tcp连接仍然建立,试图读取对方的响应数据,导致出现Software caused connection abort: recv failed的异常。 通过inputstream的available()方法来判断,是否有响应结果。但是对SocketInputStream没有效果,因为SocketInputStream 在断开连接和数据正常传输状态的下 available 返回值都是0。

3、代码分析

其中InputStream 类型为 SocketInputStream,但是SocketInputStream 在 jdk 的rt.jar 中,是JDK的核心专用类型,不是public类型,只能通过反射获取其中的参数。 有一行代码:boolean eof = (Boolean) getValueByKey(inputStream, “eof”);获取eof 字段的值就是通过Java 的反射机制做的。下面会重点说这个字段。

开启WebSocket服务端:

    
	private static void generateTCPServer() throws IOException
	{
		ServerSocket serverSocket = new ServerSocket(12345);

		while (true)
		{
			Socket socket = serverSocket.accept();
			Thread thread = new Thread()
			{
				@Override
				public void run(){
					System.out.println("开启新的线程");					
					while (true){
						try {
							if (socket == null){
								System.out.println("socket为null");
								Thread.interrupted();
							}
							else{
								boolean flag = handler(socket);
								if(!flag)
								{
									System.out.println("Client Down");
									Thread.interrupted();
									break;
								}
							Thread.sleep(50);
							}
						}
						catch (Exception e)
						{
							logger.error("System error");
							logger.error(e.getMessage());
							e.printStackTrace();
						}
						
					}
				}
			};
			thread.start();
		}
	}

连接数据处理逻辑:


	public static boolean handler(Socket socket) {
		try {
			OutputStream outputStream = socket.getOutputStream();
			InputStream inputStream = socket.getInputStream();
			// 接受客户端的响应
			byte[] b = new byte[51200];
			inputStream.read(b);

			// 第一种方式:通过获取eof 参数获取当前连接是否断开的参数
			boolean eof = (Boolean) getValueByKey(inputStream, "eof");
			if (true == eof) {
				// 已经断开
				logger.error("");
				return false;
			}
			
			// 第二种方式:获取传输的内容
			String msg = CommonUtil.toHexString(b);
			String head = msg.substring(0, 2);
			// client断开
			if (head.equals("00")) {
				logger.error("");
				return false;
			}
			
		} catch (IOException e) {
			logger.error("IOException by TCP");
			logger.error(e);
		}
		return true;
	}

4、场景分析

4.1.建立Socket连接,底层就是TCP连接:

连接过程代码走到 inputStream.read(b);就I/O中断了 并等待请求数据过来,继续执行下面的代码。new byte[51200] 为下一次请求建立了一缓存区,用于接收下一次请求的数据。

这里Thread的状态仍然是RUNNABLE。

发起连接请求:响应 为空


流程处理过程中部分核心参数:

    OutputStream
		   append:false
		   channel:null
		   closed:false
		   closing:false
		   socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false
		InputStream
			channel:null
			closed:false 
			closing:false
			eof:false  =========== 不一样的地方
			socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false

4.2.发送数据

代码从连接检测的代码处继续执行,一直到返回响应结束。
// 第一种方式:通过获取eof 参数获取当前连接是否断开的参数
boolean eof = (Boolean) getValueByKey(inputStream, “eof”);

流程处理过程中部分核心参数:如果连接没有断开,就是上一次请求结束后的参数。

      OutputStream
		   append:false
		   channel:null
		   closed:false
		   closing:false
		   socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false
		InputStream
			channel:null
			closed:false 
			closing:false
			eof:false  =========== 不一样的地方
			socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false

4.3.断开连接

代码从连接检测的代码处继续执行,但是在连接断开检测的时候就结束了 并返回false。
// 第一种方式:通过获取eof 参数获取当前连接是否断开的参数
boolean eof = (Boolean) getValueByKey(inputStream, “eof”);
以下两种方式中的任意一种都可以作为连接断开检测代码,推荐第一种:

流程处理过程中部分核心参数:连接断开后eof为true.

       OutputStream
		   append:false
		   channel:null
		   closed:false
		   closing:false
		   socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false
		InputStream
			channel:null
			closed:false 
			closing:false
			eof:true  ============不一样的地方
			socket:
			  bound:true
			  created:true
			  connected:true
			  closed:false

5、总结:

优先通过连接状态来判断客户端是否断开。需要建立一种异常检测机制和连接重连的机制。