TCP(Transmission Control Protocol,传输控制协议)是互联网协议套件中的一个核心协议之一,用于在网络上可靠地传输数据。TCP提供了可靠的、面向连接的数据传输服务,它确保数据按照发送顺序到达目的地,并且没有损坏或丢失.
本文章将分成几部分去介绍它.
目录
一.TCP协议段格式
TCP主要由以下部分组成:
-
源端口(Source Port)和目的端口(Destination Port):分别是16位的字段,用于标识发送方和接收方的应用程序或服务。
-
序列号(Sequence Number):32位字段,用于标识TCP数据包中第一个字节的序列号。序列号指示了数据包中数据的起始位置。
-
确认号(Acknowledgment Number):32位字段,仅在ACK标志位被置为1时有效。表示期望收到的下一个序列号,用于确认接收方已成功接收到数据。
-
数据偏移(Data Offset):4位字段,指示TCP头部的长度,以32位字为单位。因为TCP头部的长度是可变的,该字段用于指示TCP头部的起始位置。
-
保留位(Reserved):6位字段,保留供将来使用,目前必须置为0。
-
控制位(Flags):用于指示TCP数据包的控制信息,其中常见的标志位包括:
- URG:表示紧急指针字段是否有效。
- ACK:表示确认号字段是否有效。
- PSH:表示接收方应该尽快将数据交给应用层而不是等待缓冲区填满。
- RST:表示连接复位。
- SYN:用于建立连接。
- FIN:用于释放连接。
-
窗口大小(Window Size):16位字段,表示发送方期望接收到的字节数,用于流量控制。
-
校验和(Checksum):16位字段,用于检测TCP头部和数据的完整性,以及在传输过程中是否发生了错误。
-
紧急指针(Urgent Pointer):16位字段,仅在URG标志位被置为1时有效。用于指示紧急数据的结束位置。
-
选项(Options):可选字段,用于提供额外的功能或配置参数。
-
数据(Data):可选字段,包含TCP数据包的有效载荷。
二.TCP的原理
TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。
1.确认应答机制(安全机制)
当接收方成功接收到TCP数据包时,会向发送方发送确认消息,告知发送方已经收到了数据。确认消息中包含一个确认号字段(ACK),指示接收方期望接收到的下一个字节的序列号。通过确认应答,发送方可以了解到哪些数据已经成功地传输到了接收方,以及接收方期望接收到的下一个数据的位置
2.超时重传机制(安全机制)
TCP的超时重传机制是为了保证数据的可靠传输而设计的一种机制。在TCP中,当发送方发送数据包后,会启动一个定时器,等待接收方发送确认消息。如果在规定的时间内没有收到确认消息,就会认为数据包丢失,触发超时重传机制,重新发送丢失的数据包。
但是,主机A未收到B发来的确认应答,也可能是因为ACK丢失了
因此主机B会收到很多重复数据。那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。 这时候我们可以利用前面提到的序列号就可以很容易做到去重的效果。
3.连接管理机制(安全机制)
连接管理机制简单来说就是我们常称的三次握手和四次挥手
三次握手(Three-Way Handshake)
三次握手是指在建立一个TCP连接时,需要进行三次通信:
- 客户端向服务器发送连接请求:客户端发送一个带有 SYN(同步序列编号)标志的数据包给服务器,并进入 SYN_SENT 状态,表示请求连接。
- 服务器响应并接受连接请求:服务器收到客户端的 SYN 包后,如果同意建立连接,会发送一个带有 SYN/ACK 标志的数据包作为应答,并进入 SYN_RECV 状态。
- 客户端确认连接请求:客户端收到服务器的 SYN/ACK 包后,会发送一个带有 ACK 标志的数据包给服务器,表示连接已建立,双方进入 ESTABLISHED 状态,开始正式传输数据。
这样,TCP连接就建立起来了,双方可以开始进行数据传输。
四次挥手(Four-Way Handshake)
四次挥手是指在关闭一个TCP连接时,需要进行四次通信:
- 客户端发送连接释放报文:当客户端想要关闭连接时,会发送一个带有 FIN(连接释放)标志的数据包给服务器,并进入 FIN_WAIT_1 状态,表示客户端不再发送数据,但仍可接收数据。
- 服务器确认收到释放连接请求:服务器收到客户端发送的 FIN 包后,会发送一个 ACK 包作为应答,并进入 CLOSE_WAIT 状态,此时客户端到服务器的连接已经关闭,但服务器到客户端的连接仍然存在。
- 服务器关闭连接:当服务器确定数据传输完成后,会发送一个带有 FIN 标志的数据包给客户端,并进入 LAST_ACK 状态,表示服务器不再发送数据,但仍可以接收数据。
- 客户端确认收到关闭连接请求:客户端收到服务器的 FIN 包后,会发送一个 ACK 包作为应答,并进入 TIME_WAIT 状态,等待一段时间后关闭连接,此时服务器到客户端的连接也被关闭。
4.滑动窗口(效率机制)
在TCP协议中,滑动窗口是一种用于流量控制和可靠传输的机制,它允许发送方在不等待确认的情况下持续发送数据,以提高数据传输的效率。TCP滑动窗口的主要作用是调整发送方和接收方之间的数据流量,以确保网络的稳定性和高效性。
滑动窗口的工作原理如下:
-
发送方窗口(Sender Window): 发送方维护一个发送窗口,用于存放已发送但未被确认的数据段。发送窗口的大小由接收方的接收窗口大小和网络状况动态调整。
-
接收方窗口(Receiver Window): 接收方维护一个接收窗口,用于指示自己能够接收的数据段的范围。接收窗口大小由接收缓冲区的可用空间和应用程序的处理能力决定。
-
滑动窗口的移动: 当发送方发送数据段时,数据段将被放置在发送窗口中等待确认。一旦接收方收到数据并确认,发送窗口向前滑动,允许发送更多的数据。如果某个数据段未被确认,发送窗口将保持在当前位置,直到收到确认。
-
动态调整窗口大小: TCP允许发送方和接收方根据网络条件和系统资源动态调整窗口大小。通过TCP拥塞控制算法(如拥塞避免算法),发送方可以根据网络拥塞情况调整窗口大小,以避免网络拥塞。
5.流量控制(安全机制)
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发 送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。 因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control);
- 接收端将自己可以接收的缓冲区大小放入TCP首部中的 "窗口大小" 字段,通过ACK端通知发 送端;
- 窗口大小字段越大,说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接受到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一 个窗口探测数据段,使接收端把窗口大小告诉发送端。
6.拥塞控制(安全机制)
TCP的拥塞控制是一种网络流量管理机制,旨在确保在网络中发生拥塞时,TCP发送方减少数据的发送速率,从而防止网络拥塞的进一步恶化。以下是TCP拥塞控制的基本介绍:
-
慢启动(Slow Start): 当TCP连接建立时,发送方将初始拥塞窗口设置为一个较小的值(通常为一个报文段的大小)。随着时间的推移,每当发送方收到来自接收方的确认消息,拥塞窗口大小就会增加。初始阶段,拥塞窗口指数增长,即每经过一个往返时间(RTT),拥塞窗口大小翻倍。这样可以使发送方迅速将数据推向网络,但在网络容量未知的情况下可能导致拥塞。
-
拥塞避免(Congestion Avoidance): 一旦拥塞窗口达到某个阈值(拥塞避免阈值),发送方切换到拥塞避免状态。在拥塞避免状态下,拥塞窗口大小以线性增长(每个 RTT 增加一个报文段的大小),而不是指数增长。这样可以更稳健地调整发送方的发送速率,避免引发网络拥塞。
-
快速重传(Fast Retransmit): 如果发送方连续收到相同的确认消息(指示接收方期望收到的下一个序号),则表明有数据包丢失。在这种情况下,发送方不需要等待超时,而是立即重传丢失的数据包。这样可以更快地恢复丢失的数据包,减少网络拥塞的影响。
-
快速恢复(Fast Recovery): 一旦发送方检测到丢失数据包,并执行了快速重传,它会将拥塞窗口减半,然后切换到快速恢复状态。在快速恢复状态下,拥塞窗口大小增加一个报文段的大小,而不是减半。这样可以更快地适应网络状况,减少拥塞对性能的影响。
7.延迟应答(效率机制)
延迟应答是指TCP协议中用于处理接收方暂时不准备发送确认的情况:
-
接收方延迟确认: 在TCP连接中,接收方不会立即发送确认消息以响应收到的数据。相反,它可能会等待一段时间,以便将确认消息合并为一个单独的确认,或者等待接收到更多的数据。这种延迟确认的主要目的是减少确认消息的数量,从而降低网络的负载和提高网络利用率。
-
ACK合并: 接收方可以合并多个收到的数据报文的确认信息,然后一次性发送确认。这样可以减少确认消息的数量,减轻网络负载,并且可以更有效地利用网络带宽。
-
应用层缓冲: 在接收方的应用程序可能会设置一个缓冲区,用于缓存接收到的数据,而不是立即向发送方发送确认。这样可以降低确认消息的发送频率,从而降低网络开销。
-
等待数据: 接收方可能会等待一段时间,以便接收更多的数据后再发送确认。这种做法可以提高网络的效率,因为确认消息往往会导致发送方进入慢启动或拥塞避免状态,从而减少了发送方的发送速率。
8.延迟应答(效率机制)
CP的捎带应答是指在TCP连接中,接收方收到数据后,如果有需要发送的确认消息而且刚好有数据要发送,就会利用这次发送机会,将确认消息“捎带”在数据包中一起发送,而不是单独发送确认消息。这样可以利用网络带宽,减少确认消息所占用的网络资源,提高网络的利用率。
三.基于TCP应用层协议
基于TCP协议的应用层协议有很多,其中一些常见的包括:
-
HTTP (Hypertext Transfer Protocol):用于在客户端和服务器之间传输超文本的应用层协议。HTTP是Web中最常见的协议,用于在浏览器和Web服务器之间传输HTML页面、图像、样式表等资源。
-
FTP (File Transfer Protocol):用于在客户端和服务器之间传输文件的应用层协议。FTP允许用户在计算机之间进行文件传输,并支持上传、下载、删除文件等操作。
-
SMTP (Simple Mail Transfer Protocol):用于在邮件服务器之间传输电子邮件的应用层协议。SMTP定义了电子邮件的发送方式,包括邮件的格式、传输方式等。
-
POP3 (Post Office Protocol version 3):用于从邮件服务器上接收电子邮件的应用层协议。POP3允许用户通过邮件客户端从邮件服务器上下载电子邮件到本地计算机上。
-
IMAP (Internet Message Access Protocol):也是用于从邮件服务器上接收电子邮件的应用层协议。与POP3不同的是,IMAP允许用户在邮件服务器上管理邮件,包括创建文件夹、标记邮件等操作。
-
Telnet:用于远程登录到远程主机的应用层协议。Telnet允许用户通过网络远程控制远程主机,并在远程主机上执行命令和操作。
-
SSH (Secure Shell):也用于远程登录到远程主机的应用层协议,但是相比Telnet更安全,因为SSH使用加密技术来保护传输的数据。
-
DNS (Domain Name System):用于将域名解析为IP地址的应用层协议。DNS允许用户通过域名访问互联网资源,而不需要记住复杂的IP地址。
四.通过JAVA实现TCP通信的前提准备
要实现TCP编程,我们首先要了解JAVA中与TCP有关的几个类
1.ServerSocket API
Java 中的 ServerSocket 类是用于实现服务器端套接字的,它允许 Java 程序监听客户端的连接请求,并在连接请求到达时创建对应的 Socket 对象,从而与客户端进行通信。下面是 ServerSocket 类的一些重要方法和功能:
-
构造函数:
ServerSocket(int port)
: 创建一个 ServerSocket 实例,并指定要监听的端口号。
-
主要方法:
accept()
: 该方法用于接受客户端的连接请求,并返回一个代表该连接的 Socket 对象。如果没有连接请求到达,该方法会一直阻塞直到有连接请求到达为止。close()
: 关闭 ServerSocket。一旦关闭,ServerSocket 将不再接受新的连接请求。getInetAddress()
: 返回 ServerSocket 绑定的本地地址。getLocalPort()
: 返回 ServerSocket 绑定的本地端口号。setSoTimeout(int timeout)
: 设置接受连接请求的超时时间,单位为毫秒。如果在指定的超时时间内没有连接请求到达,accept() 方法将会抛出 SocketTimeoutException 异常。
2.Socket API
Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。
-
构造函数:
- Socket(String host, int port): 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的 进程建立连接
-
主要方法:
- InetAddress getInetAddress(): 返回套接字所连接的地址
- InputStream getInputStream(): 返回此套接字的输入流
- OutputStream getOutputStream(): 返回此套接字的输出流
在开始编程之前我们还需要了解TCP的两种连接方式
3.TCP中的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接.也就是说,短连接只能一次收发数 据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接.也就是说,长连接可以多次收发数据。
五.用JAVA实现TCP通信
一发一收(短连接)
一个客户端一次数据发送,和服务端多次数据接收(一次发送一次接收,可以接收多次),即只 有客户端请求,但没有服务端响应的示例
TCP服务端
代码如下:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer1 {
//服务器socket要绑定固定的端口号8888
private static final int PORT=8888;
public static void main(String[] args) throws IOException {
//用来监听的类ServerSocket,它会检查是否有来自客户端的请求并返回与客户端的连接
ServerSocket socket=new ServerSocket(PORT);
//不停的等待客户端连接
while (true){
//当使用accept方法后,线程会一直等待,直到有客户端的连接,并返回交给client
Socket client=socket.accept();
//有了和客户端的连接client后,打印客户端的相关信息
System.out.printf("客户端IP:%s%n",client.getInetAddress().getHostAddress());
System.out.printf("客户端端口号:%s%n",client.getPort());
//为了接收客户端的数据,获取连接的数据流is
InputStream is= client.getInputStream();
//为了方便获取字符串内容,可以将以上字节流包装为字符流,即接收的时候可以以字符输出
BufferedReader br=new BufferedReader(new InputStreamReader(is,"UTF-8"));
//将获取的数据放到line中
String line;
//一直读取到流结束
while ((line=br.readLine())!=null){
System.out.printf(line);
}
//关闭与客户端的连接
client.close();
}
}
}
TCP客户端
代码如下:
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
public class TcpClient1 {
//服务端Socket进程的端口号
private static final int PORT=8888;
//服务端IP或域名
private static final String SERVER_HOST="localhost";
public static void main(String[] args) throws IOException {
//创建一个客户端流套接字Socket
//创建好了自动与对应IP的主机上,对应端口的进程建立连接
Socket client=new Socket(SERVER_HOST,PORT);
//通过socket中的输出流进行发送数据
OutputStream os= client.getOutputStream();
//为了方便输出字符串作为发送的内容,可以将以上字节流包装为字符流
PrintWriter pw=new PrintWriter(new OutputStreamWriter(os,"UTF-8"));
//输出数据到缓冲区
pw.println("hello world");
//刷新缓冲区,强制发送数据
pw.flush();
//断开与服务端的连接
client.close();
}
}
接着我们先开启服务端等待接收数据,再开启客服端发送数据,就得到如下结果:
客户端IP:127.0.0.1
客户端端口号:64415
hello world
到这里,一个最简单的TCP通信就写好了,但是这只是最基础接收和发送数据,并没有功能的实现
关于TCP的更多演示,将会放在另一篇博客中
到这里对TCP的介绍就结束了~~