zoukankan      html  css  js  c++  java
  • 从tcp层面研究java socket 的使用

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

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

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

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

    server端(192.168.104.188):

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.TimeUnit;
    
    public class Server {
        public static void main(String[] args) throws IOException, InterruptedException {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            System.out.println("the connection has opened...");
            TimeUnit.SECONDS.sleep(60);
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
            String temp = in.readLine();
            System.out.println("the server receives from client : " + temp);
            TimeUnit.SECONDS.sleep(60);
            
            out.println(temp.toUpperCase());
            System.out.println("the server sends to client HELLO ...");
            
            TimeUnit.SECONDS.sleep(60);
            socket.close();
            serverSocket.close();
        }
    }

    client端(192.168.5.51):

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.concurrent.TimeUnit;
    
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
            Socket socket = new Socket("192.168.104.188", 8888);
            System.out.println("the connection has opened...");
            TimeUnit.SECONDS.sleep(60);
            
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
            out.println("hello");
            System.out.println("the client sends hello to Server...");
            TimeUnit.SECONDS.sleep(60);
            
            System.out.println("the client receives : " + in.readLine());
            
            TimeUnit.SECONDS.sleep(60);
            socket.close();
        }
    }

    分别执行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端:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.TimeUnit;
    
    public class Server {
        public static void main(String[] args) throws IOException, InterruptedException {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            System.out.println("the connection has opened...");
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
            String temp = in.readLine();
            System.out.println("the server receives from client : " + temp);
            
            out.println(temp.toUpperCase());
            System.out.println("the server sends to client HELLO ...");
            
            TimeUnit.SECONDS.sleep(20);
         //此时client端的socket已经关闭 out.println(
    "world"); System.out.println(in.readLine()); //返回null socket.close(); serverSocket.close(); } }

    client端:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
            Socket socket = new Socket("192.168.104.188", 8888);
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
            out.println("hello");
            System.out.println("the client sends hello to Server...");
            
            System.out.println("the client receives : " + in.readLine());
            
            socket.close();
        }
    }

    在修改后的代码中,在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端:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.TimeUnit;
    
    public class Server {
        public static void main(String[] args) throws IOException, InterruptedException {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            System.out.println("the connection has opened...");
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
            TimeUnit.SECONDS.sleep(5);
            
            String temp = in.readLine(); 
            
            out.println(temp == null ? "null" : temp.toUpperCase());
            System.out.println("the server sends to client HELLO ...");
            
            socket.close();
            serverSocket.close();
        }
    }

    client端:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.concurrent.TimeUnit;
    
    public class Client {
        public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
            Socket socket = new Socket("192.168.104.188", 8888);
            
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            
            out.close();
            
            TimeUnit.SECONDS.sleep(5);
            
            try{
                PrintWriter out2 = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
                
                out2.println("hello");
                System.out.println("the client sends hello to Server...");
                
                System.out.println("the client receives : " + in.readLine());
            }catch(Exception e){
                e.printStackTrace();
            }
            
            socket.close();
        }
    }

    在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。

  • 相关阅读:
    linux学习笔记三
    linux学习笔记二
    linux学习笔记一
    Linux操作篇之配置DNS服务(二)
    Linux操作篇之配置DNS服务(一)
    Linux操作篇之配置DHCP服务
    Linux操作篇之配置SSH服务
    Linux操作篇之自动化安装操作系统(二)
    Linux操作篇之自动化安装操作系统(一)
    Linux的shell编程篇之环境变量配置文件
  • 原文地址:https://www.cnblogs.com/zh1164/p/7418567.html
Copyright © 2011-2022 走看看