java socket三次握手_TCP三次握手和四次挥手与Java Socket

本文详细介绍了TCP的三次握手和四次挥手过程,通过Java Socket实现了一个简单的服务端和客户端通信实例,解释了套接字的概念,并探讨了在实际通信中可能遇到的问题,如数据长度、ACK置位等。
摘要由CSDN通过智能技术生成

简介

想要理解 TCP 的三次握手和四次挥手和 Java Socket,首先需要掌握 TCP 的报头结构(传送门)。如下图所示:

fb27025c43a30f9bb223ef39a4fbea8b.png

00~31 表示 32 个比特位,即 32 个二进制位。

序列号 Seq:当前数据段的第一个字节的序列号。

确认编号 Ack:期望接收到下一个数据段的序列号。

紧急指针 Urgent Pointer:指向紧急数据序列中最后一个字节的序列号。

标识

描述

URG

紧急指针标识位。

ACK

确认编号标识位。

PSH

提示接收端应用程序立即从TCP缓冲区把数据取走

RST

发送方要求重新建立连接,复位

SYN

请求建立连接。

FIN

希望断开连接。

URG 设置为 1,紧急指针需要被优先处理

ACK 设置为 1,表示 确认编号 Ack 有效

SYN 设置为 1,表示 序列号 Seq 设定为随机初始值

何为套接字

在 TCP 术语中,一个 IP 地址和一个端口号的组合有时被称为 端点 (endpoint)或者 套接字 (socket)。

每个 TCP 连接由一对套接字或者端点(四元组,客户端IP,客户端端口,服务端IP,服务端端口)唯一地标识。

摘自《TCP/IP 详解卷 1》——12章第3节;另外 RFC0793 有英文原文描述

简单来说,套接字 = IP地址 + 端口号。

实战

启动 wireshark

启动 Java 服务端

public class EchoServer {

public static void main(String[] args) throws IOException {

ServerSocket serverSocket = new ServerSocket(8080);

System.out.println("服务器等待连接...");

Socket clientSocket = serverSocket.accept();

System.out.println("服务端正在接收信息...");

InputStream inStream = clientSocket.getInputStream();

OutputStream outStream = clientSocket.getOutputStream();

Scanner in = new Scanner(inStream);

PrintWriter out = new PrintWriter(outStream, true /*autoFlush*/);

out.println("Hello! Enter BYE to exit");

System.out.println("服务端正在读取信息...");

boolean done = false;

while (!done && in.hasNextLine()) {

String line = in.nextLine();

// 回声

String echo = "Echo:" + line;

out.println(echo);

if (line.trim().equals("BYE")) done = true;

}

System.out.println("服务器关闭连接...");

inStream.close();

serverSocket.close();

}

}

首先在 IDEA 中启动该 Java 服务端:

5d75f3987009fd9160a86bbb527c7898.png

如上图所示,服务端代码阻塞在了 serverSocket.accept() 等待客户端的连接

启动 Windows Telnet 客户端

Win+R 打开运行,输入 cmd 打开命令行提示符

输入 telnet 127.0.0.1 8080

09ffc013e70348866a2c1793a16e99d0.png

连接成功,接收到服务端发来的消息 Hello! Enter BYE to exit

b1a101536fe3c5cd6620733a77902c60.png

同时按下 Ctrl+],进入 Microsoft Telnet Client

f05f8448b18b61a808e510204c637cbf.png

输入 close, 按下回车,主动关闭客户端连接

146d4eed906ff666044fbe6d570e20a5.png

实验结果

4d3ca1319c28cc310918224aa35e550f.png

关于实验结果的疑问

问题一:为什么发出去的 Hello! Enter BYE to exit 是 24 个字节,但是却显示长度为 26 呢?怎么多了两个字节?

82fa4fca5e1dad651ac4d801c337881b.png

答:查询 ASCii 码表,得知 0d 表示 CR 回车,0a 表示 LF 换行/新行。out.println 在原来的基础上追加了回车和换行字符 \r\n。

问题二:除了客户机第一个发起连接请求的 SYN 报文外,其他每个报文都有 ACK 置位?

RFC0793 明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1。

追问:

TCP作为一个可靠传输协议,其可靠性就是依赖于收到对方的数据,发送ACK给对方,这样对方就可以释放缓存的数据,因为对方确信数据已经被接收到了。但TCP报文是在IP网络上传输,丢包是家常便饭,接收方要抓住一切的机会,把消息告诉发送方。最方便的方式就是,任何我方发送的TCP报文,都要捎带着ACK状态位。

问题三:我们发现,带数据(长度 Len > 0)的包都将 PSH 置位?

该标志表示发送端缓存为空。也就是说,当 PSH 置位的数据包发送完成以后,发送端没有其他数据包需要传送。

三次握手

三段握手,发生在建立连接的阶段。

8f749384cffd33d8b7081fd00deb0a56.png

CLOSED:无连接状态

LISTEN:等待任意远程 TCP 和对应端口发来的连接请求

SYN-SENT:在发出连接请求后,等待匹配的连接请求

SYN-RECEIVED:已经收到和发出连接请求,正在等待连接请求的确认

ESTABLISHED:已建立连接,可以传送数据给用户。TCP 连接的数据传输阶段的正常状态。

四次挥手

f306c2c30cb0ac14d9cdd1681c03538a.png

FIN-WAIT-1:等待来自远程主机的连接终止请求或先前发送的连接终止请求的确认。

FIN-WAIT-2:等待来自远程主机的连接终止请求。

CLOSE-WAIT:等待来自远程主机的连接终止请求。

LAST-ACK:等待先前发送给远程主机的连接终止请求的确认(其中包括其连接终止请求的确认)。

TIME-WAIT:等待足够的时间以确保远程主机收到其连接终止请求的确认。

FIN-WAIT-2 半关闭状态

已经接收到先前发送给远程主机的连接终止请求的确认,等待来自远程主机的连接终止请求,当前客户机就进入了 FIN-WAIT-2 阶段。

这是在关闭连接时,客户端和服务器两次挥手之后的状态,是著名的半关闭的状态了,在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于 FIN-WAIT-2 状态,而服务器则一直处于 CLOSE-WAIT 状态,而直到应用层来决定关闭这个状态。

Java 半关闭客户端

public class HalfCloseClient {

public static void main(String[] args) throws IOException {

Socket socket = new Socket();

socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8080));

PrintWriter out = new PrintWriter(socket.getOutputStream());

Scanner in = new Scanner(socket.getInputStream());

out.print("nice to meet you");

out.flush();

socket.shutdownOutput();

while (in.hasNextLine()) {

String line = in.nextLine();

System.out.println(line);

}

socket.close();

}

}

结果

09b2e7abc687f1a6bcf0a8b40efca8b0.png

半关闭 (half-close) 提供了这样一种能力:套接字连接的一端可以终止其输出,同时仍就可以接收来自另一端的数据。

如上图所示,客户端执行 socket.shutdownOutput() 之后,客户端进入了 FIN-WAIT-2 阶段,但是此时客户端仍然可以接收服务端传输的数据,并且还可以发送确认。

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值