细说Java Socket中的setSoLinger方法

原创 2016年09月05日 15:01:31

在Java Socket中,当我们调用Socket的close方法时,默认的行为是当底层网卡所有数据都发送完毕后,关闭连接

通过setSoLinger方法,我们可以修改close方法的行为

1,setSoLinger(true, 0)

当网卡收到关闭连接请求后,无论数据是否发送完毕,立即发送RST包关闭连接

2,setSoLinger(true, delay_time)

当网卡收到关闭连接请求后,等待delay_time

如果在delay_time过程中数据发送完毕,正常四次挥手关闭连接

如果在delay_time过程中数据没有发送完毕,发送RST包关闭连接


通过测试程序以及抓包文件详细的观察了一下这个方法的行为

客户端代码如下:

package com.test.client;

import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Client {
	private static int port = 9999;
	private static String host = "192.168.52.131";
//	private static String host = "127.0.0.1";
	
	public static SimpleDateFormat sdf = new SimpleDateFormat(
			"yy-MM-dd HH:mm:ss.SSS");
	
	public static void main(String[] args) throws Exception {
		Socket socket = new Socket();
//		CASE 1 : default setting
		socket.setSoLinger(false, 0);
//		CASE 2 :
//		socket.setSoLinger(true, 0);
//		CASE 3 :
//		socket.setSoLinger(true, 1);
		SocketAddress address = new InetSocketAddress(
				Client.host, Client.port);
		socket.connect(address);
		
		OutputStream output = socket.getOutputStream();
		StringBuilder strB = new StringBuilder();
		for(int i = 0 ; i < 10000000 ; i++ ){
			strB.append("a");
		}
		byte[] request = strB.toString().getBytes("utf-8");
		System.out.println("Client before write : " + sdf.format(new Date()));
		output.write(request);
		System.out.println("Client after write : " + sdf.format(new Date()));
		socket.close();
		System.out.println("Client after close : " + sdf.format(new Date()));
	}
}

服务端代码如下:

package com.test.server;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Server {
	private static int queueSize = 10;
	private static int port = 9999;
	
	public static SimpleDateFormat sdf = new SimpleDateFormat(
			"yy-MM-dd HH:mm:ss.SSS");
	
	public static void main(String[] args) throws Exception {
		ServerSocket serverSocket = new ServerSocket();
		serverSocket.setReuseAddress(true);
		serverSocket.setReceiveBufferSize(128*1024);
		serverSocket.bind(new InetSocketAddress(Server.port), 
				Server.queueSize);
		
		Socket socket = null;
		while(true){
			socket = serverSocket.accept();
			InputStream input = socket.getInputStream();
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int length = -1;
			Thread.sleep(10*1000);
			System.out.println("Server before read : " + sdf.format(new Date()));
			while((length = input.read(buffer)) != -1){
				output.write(buffer, 0, length);
			}
			System.out.println("Server after read : " + sdf.format(new Date()));
			String req = new String(output.toByteArray(), "utf-8");
			System.out.println(req.length());
			
			socket.close();
		}
	}
}


CASE 1

客户端运行结果:

Client before write : 16-09-01 16:49:45.396
Client after write : 16-09-01 16:49:55.307
Client after close : 16-09-01 16:49:55.307

服务端运行结果:

Server before read : 16-09-01 16:49:55.558
Server after read : 16-09-01 16:49:55.680
10000000

抓包结果如下:

虽然客户端在16:49:55.307(客户端运行结果)就已经关闭了连接,但是直到16:49:55.680107(抓包文件718行)客户端才发出FIN包开始关闭连接

即底层会等待数据发送完毕才关闭连接


CASE 2

客户端运行结果:

Client before write : 16-09-01 17:34:37.019
Client after write : 16-09-01 17:34:46.876
Client after close : 16-09-01 17:34:46.877

服务端运行结果:

Server before read : 16-09-01 17:34:47.107
Exception in thread "main" java.net.SocketException: Connection reset
	at java.net.SocketInputStream.read(SocketInputStream.java:196)
	at java.net.SocketInputStream.read(SocketInputStream.java:122)
	at java.net.SocketInputStream.read(SocketInputStream.java:108)
	at com.test.server.Server.main(Server.java:34)

抓包结果如下:

虽然客户端在17:34:46.877(客户端运行结果)就已经关闭了连接,但是从抓包文件中可以看到,在17:34:47.238493(抓包文件906行),依然有数据从客户端发往服务端

当然代码中的关闭连接请求发送至内核、内核通过驱动控制网卡关闭连接,这一系列操作也需要不少时间,因此关闭连接请求有延迟也是正常的

可以看到服务端至接收了9442408字节连接就关闭了

当网卡接收到关闭连接请求后,即便数据并没有完全发送完毕,网卡也会立即关闭连接

此时连接通过客户端发送RST包的方式强行关闭连接,而不是通过TCP的四次挥手方式正常关闭

如果数据能够发送完毕呢?修改一下客户端代码

		for(int i = 0 ; i < 100 ; i++ ){
			strB.append("a");
		}

然后再次抓包观察

可以看到这次所有数据都发送完成

但是客户端依然会发送RST包关闭连接

关键问题是,数据能不能全部发送完,这并不是我们能控制的


CASE 3

客户端运行结果:

Client before write : 16-09-01 18:37:30.523
Client after write : 16-09-01 18:37:40.419
Client after close : 16-09-01 18:37:40.424

服务端运行结果:

Server before read : 16-09-01 18:37:40.655
Server after read : 16-09-01 18:37:40.786
10000000

抓包结果如下:

从抓包结果可以看到这里是正常关闭

即当网卡收到关闭连接请求时,等待一段时间,然后关闭连接

如果在等待的过程中,数据发送完毕,则通过四次挥手的方式正常关闭连接,否则和CASE 2一样,通过发送RST包的方式强行关闭连接


有很多地方介绍可以通过setSoLinger方法避免主动关闭方处于time wait状态,其实只有通过直接发送RST包关闭连接,而不是通过正常四次挥手的方式才能避免主动关闭方处于time wait状态,但是从上面的CASE中可以看到,发送RST包的时候,数据有时是不完整的,所以不应该使用这种方式来避免主动关闭方处于time wait状态


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

网络编程:优雅关闭socket/TIME_WAIT/CLOSE_WAIT/SoLinger

一个很特别的参数,影响关闭socket后的行为,是立即释放,还是进入TIME_WAIT状态并等待一段时间(单位:秒)才释放。这个参数,在Socket中可以设置,在Mina2的IoService中也有s...

CLOSE_WAIT导致服务端无法正常连接(JAVA、SOCKET相关)

CLOSE_WAIT导致无法连接服务端 问题描述 1. 项目部署到linux 服务器之后,运行一段时间(大概几个钟),会开始出现一些未释放的TCP连接(状态CLOSE_WAIT) ...

java Socket 短连接和长连接的区别

长连接与短连接         所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持。      ...

JAVA Socket用法详解

本篇文章观点和例子来自 《Java网络编程精解》, 作者为孙卫琴, 出版社为电子工业出版社。      在客户/服务器通信模式中, 服务器端需要创建监听端口的 ServerSocket, Server...

socket:close_wait状态和time_wait状态问题

不久前,我的Socket Client程序遇到了一个非常尴尬的错误。它本来应该在一个socket长连接上持续不断地向服务器发送数据,如果socket连接断开,那么程序会自动不断地重试建立连接。有一天发...

java socket参数详解:SoLinger

启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。最大超时值是特定于平台的。 该设置仅影响套接字关闭。默认值为-1,表示禁用。 这个Socket选项可以影响close方法的行为。在默认...

java网络编程Socket中SO_LINGER选项的用法解读

1:设置该选项: public void setSoLinger(boolean on, int seconds) throws SocketException;     读取该选项:public i...

JAVA 网络编程(5) SOCKET UDP 单播和组播,以及组播其他机器收不到报文的解决方法

UDP既可以单播也可以组播 一,单播 单播的过程为 发送方: InetAddress destAddr = InetAddress.getByName("192.168.4.199");// 发送的目...
  • kkgbn
  • kkgbn
  • 2016-08-19 12:13
  • 1303

Java中Socket通信的基本方法

从Socket中读取数据 创建Socket连接到Server Socket chatSocket = new Socket("127.0.0.1", 5000);创建Socket的时候需要用到两个重要...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)