细说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状态


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

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

1:设置该选项: public void setSoLinger(boolean on, int seconds) throws SocketException;     读取该选项:public i...
  • woshisap
  • woshisap
  • 2011年06月30日 11:22
  • 18335

java socket参数详解:SoLinger

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

setsockopt :SO_LINGER 选项设置

 setsockopt 设置 SO_LINGER 选项    此选项指定函数close对面向连接的协议如何操作(如TCP)。内核缺省close操作是立即返回,如果有数据残留在套接口缓冲区中则系统将试着...
  • factor2000
  • factor2000
  • 2009年02月23日 17:55
  • 60898

Java Socket简例

Socket io工具类: import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream;...
  • a19881029
  • a19881029
  • 2013年09月18日 11:35
  • 7900

网络通信之Socket小结

最近在写一个关于数据通信系列的文章,所以Socket是少不了的,今天就和大家来简单分享下Socket的使用方式,以及关于Socket的几个比较重要,容易被小伙伴们忽略且常用的方法, 好了,进入今天的...
  • mythace
  • mythace
  • 2016年08月24日 22:34
  • 363

Java中Socket阻塞的原因

Java中Socket阻塞的原因 对于用ServerSocket 及 Socket 编写的服务器程序和客户程序, 他们在运行过程中常常会阻塞. 例如, 当一个线程执行 Ser...
  • u011637069
  • u011637069
  • 2015年09月22日 23:06
  • 470

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

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

细说Java Socket中的setSoLinger方法

在Java Socket中,当我们调用Socket的close方法时,默认的行为是当底层网卡所有数据都发送完毕后,关闭连接,通过setSoLinger方法,我们可以修改close方法的行为 1,set...
  • a19881029
  • a19881029
  • 2016年09月05日 15:01
  • 2475

HttpClient源码解析系列:第四篇:Connection是怎么生成和管理的

HttpClient中,Connection是怎么生成和管理的
  • qijiqiguai
  • qijiqiguai
  • 2017年08月08日 18:45
  • 215

细说socket

1. 理解socket的原理 2. 它的应用 3.
  • yangyi2083334
  • yangyi2083334
  • 2014年10月22日 10:58
  • 750
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:细说Java Socket中的setSoLinger方法
举报原因:
原因补充:

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