TCP
TCP通信原理
TCP发送数据
步骤
构造方法
相关方法
TCP接收数据
步骤
构造方法
相关方法
代码实现
注意事项
三次握手
简述
作用
详细描述
为什么要进行三次握手?
四次挥手
描述
注意
TCP
TCP通信原理
TCP发送数据
步骤
(1)创建客户端Socket对象与指定服务端连接
Socket(String host,int port)
(2)获取输出流,写数据
OutputStream getOutputStream()
(3)释放资源
void close()
构造方法
方法名 | 说明 |
---|
Socket(InetAddress address,int port) | 创建流套接字并将其连接到指定IP指定端口号 |
Socket(String host, int port) | 创建流套接字并将其连接到指定主机上的指定端口号 |
相关方法
方法名 | 说明 |
---|
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
TCP接收数据
步骤
(1)创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
(2)监听客户端连接,返回一个Socket对象
Socket accept()
(3)获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
构造方法
方法名 | 说明 |
---|
ServletSocket(int port) | 创建绑定到指定端口的服务器套接字 |
相关方法
方法名 | 说明 |
---|
Socket accept() | 监听要连接到此的套接字并接受它 |
代码实现
发送端
public class ClientDemo04 {public static void main(String[] args) throws IOException {//1.创建一个Socket对象Socket socket = new Socket("127.0.0.1",10000);//2.获取一个IO流,开始写数据OutputStream os = socket.getOutputStream();os.write("hello".getBytes());//3.释放资源os.close();socket.close();}
}
接收端
public class ServerDemo04 {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(10000);Socket accept = serverSocket.accept();InputStream is = accept.getInputStream();int b;while ((b = is.read()) != -1) {System.out.println((char)b);//h//e//l//l//o}is.close();serverSocket.close();}
}
注意事项
-
accept方法是阻塞的,作用就是等待客户端连接
-
客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
-
针对客户端来讲,是往外写的,所以是输出流 ;针对服务器来讲,是往里读的,所以是输入流
-
read方法也是阻塞的
-
客户端在关流的时候,还多了一个往服务器写结束标记的动作
-
最后一步断开连接,通过四次挥手协议保证连接终止
三次握手
简述
第一次握手:客户端给服务器发送一个 SYN 报文。
第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。
第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。
服务器收到 ACK 报文之后,三次握手建立完成。
作用
为了确认双方的接收与发送能力是否正常。
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
因此,需要三次握手才能确认双方的接收与发送能力是否正常。
详细描述
刚开始客户端处于 closed 的状态,服务端处于 listen 状态。然后
(1)第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_Send 状态。
(2)第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
(3)第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
(4)服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。
(1)SYN=1 表示该报文不携带数据,但消耗一个序号 seq=x,seq=x是客户端的初始化序列号,因为tcp是面向字节流的
(2)SYN=1 表示该报文不携带数据,但消耗一个序号 seq=y,seq=y是服务器的初始化序列号,ACK=1是一个确认号 ack=x+1,表示服务器下次接收到的序号希望是x+1。然后服务器进入到SYN-RCVD等待的状态
(3)ACK=1是一个确认号,seq=x+1是上一次服务器回应的序号要求,ack=y+1表示客户下一次接收到的序号希望是y+1
为什么要进行三次握手?
当进行第一次握手,网络不好可能会堵塞,所以连接的请求并没有到达服务器端;
但是tcp连接有超时重传的机制,所以再一次发送请求,这时候服务器端接收到了你的请求,他也会返回一个请求给你,这是第二次握手;
但是这时候网络环境突然又好了起来,那个堵塞的请求到达了服务器端,服务器端又给你回了一个请求,但是你又不想给服务器发送请求,这时候服务器的资源会进行占用等待你的请求,为了不使服务器的资源继续占用,你又必须发送一个请求给服务器;所以要进行3次握手
四次挥手
描述
1、第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。
2、第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。
3、第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
4、第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态
5、服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
注意
这里特别需要主要的就是TIME_WAIT这个状态了,这个是面试的高频考点,就是要理解,为什么客户端发送 ACK 之后不直接关闭,而是要等一阵子才关闭。这其中的原因就是,要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 ACK 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。
至于 TIME_WAIT 持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到 FIN 报文,则代表对方成功就是 ACK 报文,此时处于 CLOSED 状态。