本文主要通过wireshark抓包来分析java socket程序的一些细节, 解决以前的一些疑问:

1.当一方的socket先关闭后,另一方尚未关闭的socket 还能做什么?

2.当基于socket的流关闭后,socket 还能使用吗?

首先给出基本的server和client端代码(为了便于分析,在代码中有很多sleep代码)。

server端(192.168.104.188):

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class Server {
  11. public static void main(String[] args) throws IOException, InterruptedException {
  12. ServerSocket serverSocket = new ServerSocket(8888);
  13. Socket socket = serverSocket.accept();
  14. System.out.println("the connection has opened...");
  15. TimeUnit.SECONDS.sleep(60);
  16.  
  17. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  18. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  19.  
  20. String temp = in.readLine();
  21. System.out.println("the server receives from client : " + temp);
  22. TimeUnit.SECONDS.sleep(60);
  23.  
  24. out.println(temp.toUpperCase());
  25. System.out.println("the server sends to client HELLO ...");
  26.  
  27. TimeUnit.SECONDS.sleep(60);
  28. socket.close();
  29. serverSocket.close();
  30. }
  31. }

client端(192.168.5.51):

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class Client {
  11. public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
  12. Socket socket = new Socket("192.168.104.188", 8888);
  13. System.out.println("the connection has opened...");
  14. TimeUnit.SECONDS.sleep(60);
  15.  
  16. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  17. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  18.  
  19. out.println("hello");
  20. System.out.println("the client sends hello to Server...");
  21. TimeUnit.SECONDS.sleep(60);
  22.  
  23. System.out.println("the client receives : " + in.readLine());
  24.  
  25. TimeUnit.SECONDS.sleep(60);
  26. socket.close();
  27. }
  28. }

分别执行server和client,抓包结果如下:

分析:

1. 序号 81、82、83 对于着 socket建立(tcp建立)的三次握手。

2. 序号 809、810 对于着client向server发送了 “hello” , server向client 响应ack。

3. 序号 1557、1561 、1562对于着server向client发送的"HELLO", client 向server响应ack ,其中1561发生了一次tcp 重传。

4. 序号 2366 、2367 、 2368 对于着双方socket的关闭(tcp 连接的关闭)。

上面的过程应该没有什么问题,现在我们修改代码来研究一下刚开始时提出的问题

问题1: 其中一方的socket先于另一方关闭后,另一方socket还能做什么?

server端:

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class Server {
  11. public static void main(String[] args) throws IOException, InterruptedException {
  12. ServerSocket serverSocket = new ServerSocket(8888);
  13. Socket socket = serverSocket.accept();
  14. System.out.println("the connection has opened...");
  15.  
  16. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  17. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  18.  
  19. String temp = in.readLine();
  20. System.out.println("the server receives from client : " + temp);
  21.  
  22. out.println(temp.toUpperCase());
  23. System.out.println("the server sends to client HELLO ...");
  24.  
  25. TimeUnit.SECONDS.sleep(20);
         //此时client端的socket已经关闭
  26. out.println("world");
  27. System.out.println(in.readLine()); //返回null
  28.  
  29. socket.close();
  30. serverSocket.close();
  31. }
  32. }

client端:

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8.  
  9. public class Client {
  10. public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
  11. Socket socket = new Socket("192.168.104.188", 8888);
  12.  
  13. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  14. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  15.  
  16. out.println("hello");
  17. System.out.println("the client sends hello to Server...");
  18.  
  19. System.out.println("the client receives : " + in.readLine());
  20.  
  21. socket.close();
  22. }
  23. }

在修改后的代码中,在client端的socket关闭后,server依旧会操作socket的输入输出流,我们来看看会发送什么。wireshark抓包如下:

分析:

1. 序号 107 、 108 、109 双方建立socket连接(tcp连接)

2. 序号 110 、 111 client端向server发送hello, server向client响应ACK

3. 序号 112 是server端向client发送HELLO, 注意113, 它有两个作用,首先是client向server发起断开连接请求FIN,附带着确认对上一次HELLO的响应ACK。

4. 序号115 是server端对client端断开连接FIN的确认ACK

到此时为止,client端的socket已经关闭了,等待20秒钟后,server端 调用  out.println("world"); 继续想socket输入流中写入数据,调用 in.readLine()继续读数据。

5. 序号 337 对于的就是server端继续向client端发送 world。

6. 因为此时client端的socket早已经关闭了,相应的资源都已经释放了(例如端口 59102 早已经关闭了),所以client会向server端发送RST报文,表示无法处理。

7. 序号 339 是 server端向 client端发送拆除tcp连接的FIN,同样由于client端早已经关闭了,所以如序号 340 那样,client依旧相应了一个表示服务不可用的RST报文。

总结: 当一方socket关闭后(socket.close()), 关闭一方就会发送FIN,一段时间后释放自己的相关资源 。 另一方即使仍然没有关闭,它的读写已经没有作用了,向关闭一方发送的数据会得到RST响应,而从流中读(in.readline())直接响应null。

问题2: 在socket没有关闭前,主动关闭了流,会发生什么?

server端:

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class Server {
  11. public static void main(String[] args) throws IOException, InterruptedException {
  12. ServerSocket serverSocket = new ServerSocket(8888);
  13. Socket socket = serverSocket.accept();
  14. System.out.println("the connection has opened...");
  15.  
  16. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  17. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  18.  
  19. TimeUnit.SECONDS.sleep(5);
  20.  
  21. String temp = in.readLine();
  22.  
  23. out.println(temp == null ? "null" : temp.toUpperCase());
  24. System.out.println("the server sends to client HELLO ...");
  25.  
  26. socket.close();
  27. serverSocket.close();
  28. }
  29. }

client端:

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class Client {
  11. public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
  12. Socket socket = new Socket("192.168.104.188", 8888);
  13.  
  14. PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  15. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  16.  
  17. out.close();
  18.  
  19. TimeUnit.SECONDS.sleep(5);
  20.  
  21. try{
  22. PrintWriter out2 = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
  23.  
  24. out2.println("hello");
  25. System.out.println("the client sends hello to Server...");
  26.  
  27. System.out.println("the client receives : " + in.readLine());
  28. }catch(Exception e){
  29. e.printStackTrace();
  30. }
  31.  
  32. socket.close();
  33. }
  34. }

在client端中,获取连接后从socket中获得输入输出流,然后就关闭了输出流,等待一段时间后尝试再次获得流。 server端没有什么变化。

分析:

1. 序号 71 、 72、 73 双方socket连接(tcp连接)建立。

2. 在建立连接后获得流,然后client主动关闭了流,此时对应 序号74, client 此时发起了 断开连接的FIN,注意此时其实等同于调用了socket.close(), 关闭流同样关闭了底层的连接, 等尝试再次从socket中获得流时,会报错。在收到client的FIN后,序号 75对应server端响应的ACK。

3. server端结束睡眠后,向客户端发送数据,对应序号183, 但是client端早已经关闭了,所以依然后收到 RST响应。

(我不知道为什么server端直到程序结束也没有向client发送FIN 是为什么?)

 总结: 在socket关闭前,关闭socket的流相当于 直接关闭这个socket。

从tcp层面研究java socket 的使用的更多相关文章

  1. java Socket多线程聊天程序

    参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...

  2. 【TCP/IP】之Java socket编程API基础

    Socket是Java网络编程的基础,深入学习socket对于了解tcp/ip网络通信协议很有帮助, 此文讲解Socket的基础编程.Socket用法:①.主要用在进程间,网络间通信. 文章目录如下: ...

  3. JAVA基础知识之网络编程——-TCP/IP协议,socket通信,服务器客户端通信demo

    OSI模型分层 OSI模型是指国际标准化组织(ISO)提出的开放系统互连参考模型(Open System Interconnection Reference Model,OSI/RM),它将网络分为七 ...

  4. Java Socket实现基于TCP和UDP多线程通信

    一.通过Socket实现TCP编程 1.1 TCP编程 TCP协议是面向连接,可靠的,有序的,以字节流的方式发送数据.基于TCP协议实现网络通信的类有客户端的Socket类和服务器端的ServerSo ...

  5. JAVA Socket 底层是怎样基于TCP/IP 实现的???

    首先必须明确:TCP/IP模型中有四层结构:       应用层(Application Layer).传输层(Transport  Layer).网络层(Internet Layer  ).链路层( ...

  6. TCP/UDP套接字 java socket编程实例

    网络协议七层结构: 什么是Socket? socket(套接字)是两个程序之间通过双向信道进行数据交换的端,可以理解为接口.使用socket编程也称为网络编程,socket只是接口并不是网络通信协议. ...

  7. Socket: Java Socket 几个重要的TCP/IP选项解析(转)

    Socket选择可以指定Socket类发送和接受数据的方式.在JDK1.4中共有8个Socket选择可以设置.这8个选项都定义在java.net.SocketOptions接口中.定义如下: publ ...

  8. java socket初步学习一 ( tcp)

    Java socket通信程序: 第一版本: 实现功能: 服务器地址:127.0.0.1  端口:5050 客户机:端口5050 客户端发送字符:“t” 服务器接收到该字符并回复:“r” 流程: 建立 ...

  9. java 网络编程 TCP协议 java 服务器和客户端 java socket编程

    一个 HelloWord 级别的 Java Socket 通信的例子.通讯过程:        先启动 Server 端,进入一个死循环以便一直监听某端口是否有连接请求.然后运行 Client 端,客 ...

随机推荐

  1. 6. 抹平差异,统一类型转换服务ConversionService

    目录 ✍前言 版本约定 ✍正文 ConverterRegistry ConversionService ConfigurableConversionService GenericConversionS ...

  2. 多个table表不同数据切换 easyui中

    未处理  有效   无效  切换显示 1.加载页面时将 未处理 ,无效  有效的数据分别查到,给对应的table赋值 <%--easyui 的 tab标签栏--%><div id=& ...

  3. flowable中使用到的一些方法。获取人员部门信息

    package org.springblade.desk.utils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf ...

  4. think in java interview-高级开发人员面试宝典(一)

    "生死六重门" 无论你是在职,非在职,高级工程师,工程师,架构师,如果你正在面试阶段,请看完此文! 相信这篇文章对你的职业生涯和阶值观会造成重大的改变! 如果你是一名PM或者是管理 ...

  5. 表单序列化json字符串和js时间格式化

    js时间格式化 new Date().format("时间格式") Date.prototype.format = function(fmt) { var o = {        ...

  6. Oracle RedoLog-基本概念和组成

    Oracle 数据库恢复操作最关键的依据就是 redo log,它记录了对数据库所有的更改操作.在研究如何提取 redolog 中 DML 操作的过程可谓一波三折,因为介绍 redolog 结构细节的 ...

  7. 手动修复 under-replicated blocks in HDFS

    解决方式步骤: 1.进入hdfs的pod kubectl get pod -o wide | grep hdfs kubectl exec -ti hadoop-hdfs-namenode-hdfs1 ...

  8. JButton的常用方法

    JButton 实现了普通的三态外加选中.禁用状态,有很多方法可以设置,不要自己去写鼠标监听器.setBorderPainted(boolean b)    //是否画边框,如果用自定义图片做按钮背景 ...

  9. sqlserver 清除表数据和拷贝表结构的操作

    最近在做一个ERP系统需要导入数据,因此用到了sql的一些操作,在这里记录一下. 1.清除表数据: Delete from 表名称 where XXX 2.拷贝表结构,需求是新增一个和某个表数据格式一 ...

  10. C# 设置默认关联程序

    以下代码做个Mark /// <summary> /// Create an associaten for a file extension in the windows registry ...