【java高性能编程基础】 - TCP与UDP的详细解读

x33g5p2x  于2022-02-07 转载在 Java  
字(4.1k)|赞(0)|评价(0)|浏览(365)

概述


计算机网络体系结构中传输层的设计目标是允许源主机和目标主机上的对等实体进行对话,即为两个主机中进程之间的通信提供服务。例如,一台主机上的浏览器进程与另一台主机上的Web服务器进程之间进行通信。

TCP协议与UDP协议都是计算机网络体系结构中传输层中的网络协议,负责在程序之间传输数据(包括文本、图片、视频等数据类型)。

网络协议是指通信双方就通信如何进行所必须共同遵守的约定和通信规则的集合。在网络上通信的双方只有遵守相同的协议,才能正确地交流信息,就像人们交谈时要使用同一种语言一样,如果谈话里使用不同的语言,就会造成双方都不知所云,交流就被迫中断。

TCP

TCP是Transport Control Protocol的首字母缩写,称为传输控制协议。传输控制协议(TCP)是Internet一个重要的传输层协议。TCP提供面向连接可靠有序、****字节流传输服务。应用程序在使用TCP之前,必须先建立TCP连接。

百度百科
TCP是一种面向广域网的通信协议,目的是在跨越多个网络通信时,为两个通信端点之间提供一条具有下列特点的通信方式:
(1)基于流的方式;
(2)面向连接;
(3)可靠通信方式;
(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销;
(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点。
为满足TCP协议的这些特点,TCP协议做了如下的规定: [10]
①数据分片:在发送端对用户数据进行分片,在接收端进行重组,由TCP确定分片的大小并控制分片和重组;
②到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认;
③超时重发:发送方在发送分片时启动超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片;
④滑动窗口:TCP连接每一方的接收缓冲空间大小都固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出;
⑤失序处理:作为IP数据报来传输的TCP分片到达时可能会失序,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;
⑥重复处理:作为IP数据报来传输的TCP分片会发生重复,TCP的接收端必须丢弃重复的数据;
⑦数据校验:TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个分片,并不确认收到此报文段导致对端超时并重发。

TCP数据格式:

标志位说明:

  • URG:紧急指针
  • ACK:确认序号
  • PSH:有Data数据传输
  • RST:连接重置
  • SYN:建立连接
  • FIN:关闭连接

三次握手过程

TCP的三次握手是TCP建立连接的过程,在客户和服务器之间交换三个TCP报文段。

前面说过,应用程序在使用TCP之前,必须先建立TCP连接,建立连接就需要经过以下几个步骤:

  1. 一开始TCP的客户端和服务器都需要先创建传输控制块TCB。服务器进程就处于LISTEN(收听)状态,等待客户的连接请求。
  2. 第一次握手:客户端打算建立TCP连接时,向服务器发出连接请求报文段,同步位SYN=1,初始序列号seq=x,这时,客户端进程进入SYN-SENT(同步已发送)状态。
  3. 第二次握手:服务器收到连接请求报文段后,如果同意建立连接,则向客户端发送确认报文段。其中SYN=1ACK=1初始序号seq=y确认号ack=x+1。这时,服务端进程进入SYN-RCVD(同步收到)状态。
  4. 第三次握手:客户端收到服务器的确认后,还要向服务器确认。确认报文段ACK=1,确认号ack=y+1seq=x+1。这时,TCP连接已建立,客户端进入ESTABLISHED(已建立连接)状态,当服务器收到客户端的确认后,也进入ESTABLISHED状态。

四次挥手过程

TCP的四次握手是TCP释放连接的过程。

  1. 第一次挥手:客户端发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。终止控制位FIN=1,初始序号seq=u,这时,客户端进入FIN-WAIT-1(终止等待1)状态,等待B的确认。
  2. 第二次挥手:服务器收到连接释放报文段后即发出确认,ACK=1,确认号ack=u+1,自己的序号seq=v。这时,服务器进入CLOSE-WAIT(关闭等待)状态。
  3. 客户端收到来自服务器的确认后,进入FIN-WAIT-2(终止等待2)状态,等待服务器发出的连接释放报文段。
  4. 第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知TCP释放连接。FIN=1ACK=1,自己的序号seq=wack=u+1。这时,服务器就进入LAST-ACK(最后确认)状态。
  5. 第四次挥手:客户端在收到服务器的连接释放报文段后,必须对此发出确认。ACK=1seq=u+1ack=w+1。这时,客户端进入TIME-WAIT(时间等待)状态,经过两个最长报文段寿命(2MSL)的时间后,才进入CLOSED(关闭连接)状态。
  6. 服务器收到客户端发出的确认,就进入CLOSED状态。

UDP

UDP是User Datagram Protocol的首字母缩写,称为用户数据报协议协议。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

百度百科
UDP是OSI参考模型中一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向事务的简单不可靠信息传送服务。UDP 协议基本上是IP协议与上层协议的接口。UDP协议适用端口分别运行在同一台设备上的多个应用程序。
UDP提供了无连接通信,且不对传送数据包进行可靠性保证,适合于一次传输少量数据,UDP传输的可靠性由应用层负责。常用的UDP端口号有:53(DNS)、69(TFTP)、161(SNMP),使用UDP协议包括:TFTP、SNMP、NFS、DNS、BOOTP。
UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。

UDP数据报协议:

TCP与UDP的区别

TCPUDP
是否可靠可靠传输,使用流量控制和拥塞控制不可靠传输
是否连接面向连接(三次握手)无连接
传输方式面向字节流面向报文
连接对象个数一对一一对一,一对多,多对一,多对多
首部开销最小20字节,最大60字节,资源占用多8字节,资源占用少
适用场景要求可靠传输的应用,如文件传输等适用于实时应用(IP电话、视频会议、直播等)

Socket编程示例

Socket编程是网络通信中的一种编程技术,它的实现流程包括以下几步:

  1. 创建Socket套接字
  2. 绑定端口
  3. 发送数据
  4. 接收数据
  5. 释放Socket

Socket编程是Internet中应用最广泛的网络应用编程接口,实现与3种底层协议接口:

  • 数据报类型套接字SOCK_DGRAM(面向UDP接口)
  • 流式套接字SOCK_STREAM(面向TCP接口)
  • 原始套接字SOCK_RAW (面向网络层协议接口IP、ICMP等)

下面以TCP为例简单实现服务端与客户端的通信:

  • 服务器端
public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器启动==========");

        while(!serverSocket.isClosed()){
            Socket socket = serverSocket.accept();  //阻塞,等待连接

            System.out.println("与 " + socket.toString() + "连接成功");
            try{
                // I/O
                InputStream inputStream = socket.getInputStream();      //接收数据流
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));

                String message;
                while((message = reader.readLine())!= null){
                    if(message.length() == 0) break;
                    System.out.println(message);
                }
                System.out.println("接收到来至:" + socket.toString() + "的数据");
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

启动服务器端

  • 客户端
public class Client {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost",8888);

        OutputStream outputStream = socket.getOutputStream();

        Scanner scanner = new Scanner(System.in);
        System.out.println("输入数据:");

        String message = scanner.nextLine();
        outputStream.write(message.getBytes("UTF-8"));      //将数据转换为字节流并write

        scanner.close();
        socket.close();
    }
}

启动客户端


查看服务器端输出


客户端启动,与服务器端建立连接成功。

客户端发数据


查看服务器端输出

相关文章

微信公众号

最新文章

更多