从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。
从tcp层面研究java socket 的使用的更多相关文章
- java Socket多线程聊天程序
参考JAVA 通过 Socket 实现 TCP 编程 参考java Socket多线程聊天程序(适合初学者) 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包 ...
- 【TCP/IP】之Java socket编程API基础
Socket是Java网络编程的基础,深入学习socket对于了解tcp/ip网络通信协议很有帮助, 此文讲解Socket的基础编程.Socket用法:①.主要用在进程间,网络间通信. 文章目录如下: ...
- JAVA基础知识之网络编程——-TCP/IP协议,socket通信,服务器客户端通信demo
OSI模型分层 OSI模型是指国际标准化组织(ISO)提出的开放系统互连参考模型(Open System Interconnection Reference Model,OSI/RM),它将网络分为七 ...
- Java Socket实现基于TCP和UDP多线程通信
一.通过Socket实现TCP编程 1.1 TCP编程 TCP协议是面向连接,可靠的,有序的,以字节流的方式发送数据.基于TCP协议实现网络通信的类有客户端的Socket类和服务器端的ServerSo ...
- JAVA Socket 底层是怎样基于TCP/IP 实现的???
首先必须明确:TCP/IP模型中有四层结构: 应用层(Application Layer).传输层(Transport Layer).网络层(Internet Layer ).链路层( ...
- TCP/UDP套接字 java socket编程实例
网络协议七层结构: 什么是Socket? socket(套接字)是两个程序之间通过双向信道进行数据交换的端,可以理解为接口.使用socket编程也称为网络编程,socket只是接口并不是网络通信协议. ...
- Socket: Java Socket 几个重要的TCP/IP选项解析(转)
Socket选择可以指定Socket类发送和接受数据的方式.在JDK1.4中共有8个Socket选择可以设置.这8个选项都定义在java.net.SocketOptions接口中.定义如下: publ ...
- java socket初步学习一 ( tcp)
Java socket通信程序: 第一版本: 实现功能: 服务器地址:127.0.0.1 端口:5050 客户机:端口5050 客户端发送字符:“t” 服务器接收到该字符并回复:“r” 流程: 建立 ...
- java 网络编程 TCP协议 java 服务器和客户端 java socket编程
一个 HelloWord 级别的 Java Socket 通信的例子.通讯过程: 先启动 Server 端,进入一个死循环以便一直监听某端口是否有连接请求.然后运行 Client 端,客 ...
随机推荐
- 【Tomcat】手写迷你版Tomcat
目录 源码地址 一,分析 Mini版Tomcat需要实现的功能 二,开发--准备工作 2.1 新建Maven工程 2.2 定义编译级别 2.3 新建主类编写启动入口和端口 三,开发--1.0版本 3. ...
- 高并发redis分布式锁
1.方法一 2方法二
- Oracle中除数为0的两种解决办法(decode与nullif)
Oracle中Decode函数,语句DECODE(tag,''ZCGS'',0,1)=decode(''@corp-No@'',''6010'',1,0) decode(字段或字段的运算,值1,值2, ...
- 手写一个简单的starter组件
spring-boot中有很多第三方包,都封装成starter组件,在maven中引用后,启动springBoot项目时会自动装配到spring ioc容器中. 思考: 为什么我们springBoot ...
- Kubernetes项目简介
Kubernetes项目简介 Kubernetes 是 Google 团队发起的开源项目,它的目标是管理跨多个主机的容器,提供基本的部署,维护以及运用伸缩,主要实现语言为 Go 语言.Kubernet ...
- 编写高质量JAVA代码之让接口的职责保持单一
上述标题读者朋友应该也注意到了是让接口的职责保持单一,而不是实现者单一. 设计模式六大原则之单一原则: 定义 不要存在多于一个导致类变更的原因.**通俗的说,即一个类只负责一项职责. 下面以一个电话模 ...
- Base 128 Varints 编码(压缩算法)
Base 128 Varint可以说是一种编码方式,也可以说是一种压缩算法.这种压缩算法是用来压缩数字的传输的,压缩的依据是基于一个现实:越小的数字,越经常使用 我们来看看一个例子: 如果我们要网络传 ...
- JavaCV FFmpeg采集麦克风PCM音频数据
前阵子用一个JavaCV的FFmpeg库实现了YUV视频数据地采集,同样的采集PCM音频数据也可以采用JavaCV的FFmpeg库. 传送门:JavaCV FFmpeg采集摄像头YUV数据 首先引入 ...
- TurtleBot3 Waffle (tx2版华夫)(13)RC100遥控杆控制
13.1.遥控器说明 使用ROBOTIS RC100的设置已经在ROS的OpenCR固件中,因此不需要安装必需软件包, 安装号即可使用. 13.2.遥控器的安装 1接线口穿过后壳的过孔. 2接线口连接 ...
- linux下 shell时间处理
一.hour #获取当前时间年月日时分秒current_create_time=`date +"%Y-%m-%d %H:%M:%S"` echo $current_create_t ...