上一篇回顾了HTTP的一些基础概念,这一篇来回顾下大学时学习的TCP/UDP理论,并尝试用一些Java例子来实践下。
什么是TCP
TCP全称是Transmission Contro Protocol,翻译为传输控制协议。它被用于应用程序之间的通信,通过在两个程序之间建立”全双工”信道来达成通信。
三次握手
- 客户端发起请求
- 服务端响应请求,发送ack
- 客户端响应ack,之后通信建立成功,客户端/服务端开始通信
之所以要3次握手,是因为:
- 如果不握手,直接通信,可能服务端本身是无法通信的,所以需要确认服务端状态
- 如果只有2次握手
- 有可能在2次之后,客户端服务当机,导致服务端等待
- 有可能在2次之后,先前客户端发送的请求A延迟,但A延迟后又传递给服务端,服务端响应并建立连接。可实际上客户端认为请求A已失效,导致服务端建立的连接浪费了资源
四次挥手
在四次挥手中,可以是客户端也可以是服务端发起挥手。
- 主机A向B发起挥手请求(FIN报文)告知无后续信息发送,主机A进入FIN_WAIT_1状态
- 主机B收到A发出的请求,并发送ACK响应。主机A收到后进入FIN_WAIT_2状态
- 主机B向主机A发送FIN报文,请求关闭连接,同时进入LAST_ACK状态
- 主机A向主机B发送ACK报文,同时进入TIME_WAIT状态;主机B收到后,关闭B->A连接。主机A等待2MSL(Maximum Segment Lifetime)后,也关闭A->B连接。 之所以要4次挥手,主要是因为TCP的”全双工”特性,任何一方对对方的连接都是独立的,因此当其中某一方提出要断开连接,另一个连接可能还有待发送的消息。因此,为了将两个连接都关闭,需要挥手四次。
Java TCP
利用jdk/net包中的Socket创建客户端,ServerSocket创建服务端。
ServerSocket server = new ServerSocket(8088);
//启动之后,调用accept方法,server会阻塞并等待连接
Socket socket = server.accept();
//若有连接,则可以获取到socket的输入流
InputStream in = socket.getInputStream();
Socket socket = new Socket("localhost", 8088);
PrintWriter pw = null;
try {
pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
//调用outputStream封装PrintWriter
socket.getOutputStream())), true);
} catch (IOException e) {
e.printStackTrace();
}
//将信息写入流中
pw.println("hello server, i am client!");
什么是UDP
全称为User Datagram Protocol,是一种无连接的通信协议,消息送达后无法得知其是否安全完整到达。 它具有以下几种特点:
- 面向无连接:不建立3次握手连接,直接发送,不对报文做拆分/拼接
- 单播、多播、广播功能:不只是一对一
- 是面向报文的:UDP只添加头部就交付IP层,不拆分也不合并
- 不可靠性:无连接、且不关心对方是否收到,网络差则有丢包风险
- 头部开销小,传输高效
TCP对比UDP
TCP:
- 面向连接
- 可靠传输
- 一对一通信
- 面向字节流
- 头部开销大
- 适用于要求可靠传输的应用,如文件传输 UDP:
- 无连接
- 不可靠传输
- 一对一、一对多、广播都可支持
- 面向报文
- 头部开销小
- 适用于实时应用,如IP电话、视频会议、直播等
Java UDP
利用jdk/net中的DatagramSocket和DatagramPacket来创建和发送udp请求。
DatagramSocket dataSocket = new DatagramSocket(PORT);
DatagramPacket dataPacket = new DatagramPacket(receiveByte, receiveByte.length);
//receive方法
dataSocket.receive(dataPacket);
//接着对之前packet中的byte做string实例化从而获取消息
new String(receiveByte, 0, length);
//同样是用DatagramSocket初始化一个数据表socket
dataSocket = new DatagramSocket(PORT + 1);
sendStr = "hi server, i am client!";
sendDataByte = sendStr.getBytes();
//实例化DatagramPacket,并传入发送数据、目的地地址
dataPacket = new DatagramPacket(sendDataByte, sendDataByte.length,
InetAddress.getByName("localhost"), PORT);
//调用send方法发送
dataSocket.send(dataPacket);
参考: