Java回望(7)-tcp/udp/socket基础概念

上一篇回顾了HTTP的一些基础概念,这一篇来回顾下大学时学习的TCP/UDP理论,并尝试用一些Java例子来实践下。

什么是TCP

TCP全称是Transmission Contro Protocol,翻译为传输控制协议。它被用于应用程序之间的通信,通过在两个程序之间建立”全双工”信道来达成通信。

三次握手

  1. 客户端发起请求
  2. 服务端响应请求,发送ack
  3. 客户端响应ack,之后通信建立成功,客户端/服务端开始通信 之所以要3次握手,是因为:
    • 如果不握手,直接通信,可能服务端本身是无法通信的,所以需要确认服务端状态
    • 如果只有2次握手
    • 有可能在2次之后,客户端服务当机,导致服务端等待
    • 有可能在2次之后,先前客户端发送的请求A延迟,但A延迟后又传递给服务端,服务端响应并建立连接。可实际上客户端认为请求A已失效,导致服务端建立的连接浪费了资源

四次挥手

在四次挥手中,可以是客户端也可以是服务端发起挥手。

  1. 主机A向B发起挥手请求(FIN报文)告知无后续信息发送,主机A进入FIN_WAIT_1状态
  2. 主机B收到A发出的请求,并发送ACK响应。主机A收到后进入FIN_WAIT_2状态
  3. 主机B向主机A发送FIN报文,请求关闭连接,同时进入LAST_ACK状态
  4. 主机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,是一种无连接的通信协议,消息送达后无法得知其是否安全完整到达。 它具有以下几种特点:

  1. 面向无连接:不建立3次握手连接,直接发送,不对报文做拆分/拼接
  2. 单播、多播、广播功能:不只是一对一
  3. 是面向报文的:UDP只添加头部就交付IP层,不拆分也不合并
  4. 不可靠性:无连接、且不关心对方是否收到,网络差则有丢包风险
  5. 头部开销小,传输高效

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);

参考: