UDP 和 TCP 网络知识和“Udp协议的回声服务器详细剖析”(5000字详细解析回升服务器的点点滴滴)(网络编程01)

目录

应用层

一、传输层的两个核心协议

二、有链接/无连接(是抽象的概念,是虚拟的/逻辑上的连接),例如要进行网络通信,物理上的连接(网线啥的)

[三、可靠传输 vs 不可靠传输](#三、可靠传输 vs 不可靠传输)

[四、面向字节流 vs 面向数据报](#四、面向字节流 vs 面向数据报)

[五、全双公 vs 半双工](#五、全双公 vs 半双工)

六、回升服务器(重点)


应用层

操作系统提供的一组 API => socket API (传输层给应用层提供);socket 这个词的意思是插槽的意思,对应在主板上可以理解为主板上的一些接口。

一、传输层的两个核心协议

TCP协议 和 UDP协议这两个差别非常大。

  • TCP 是有连接,可靠传输,面向字节流,全双工
  • UDP 是无连接,不可靠传输,面向数据报,全双工

二、有链接/无连接(是抽象的概念,是虚拟的/逻辑上的连接),例如要进行网络通信,物理上的连接(网线啥的)

  • 为什么TCP 是可靠的传输(有链接),而 UDP 是不可靠传输(无连接)。

对于 TCP 来说,TCP 协议中,就会保存对端的信息,例如 A和B通信,A和B先建立连接,让A保存B的信息,B保存A的信息(彼此之间知道,谁是和它连接的哪个)。

对于UDP来说,UDP协议本身,不保存对方的信息。就是无连接 。

三、可靠传输 vs 不可靠传输

网络上数据是非常容易出现丢失的情况(丢包) ,因为光信号/电信号 ,都可能受到外界的干扰。本来是传输0101,但是因为数据的丢包,其中有些bit位就被修改了,这样乱了的数据就会被识别出来,把这样的数据极可能传输不到原来的地方。

而且网络世界是通过路由器/交换机 来进行联络的,路由器/交换机 类似于"十字路口 ",它也会发生我们生活中十字路口堵车的情况,(在某个时间点,实际需要转发的数据超过了设备能转发的上线),就会容易发生数据丢包问题。

  • 可靠传输 的意思是,不能保证数据包100% 的到达,而是尽可能的提高传输成功的概率如果出现了丢包,能够感知到。
  • 不可靠传输,只是把数据发出去,他到底是到了还是没到,他就不管了。

四、面向字节流 vs 面向数据报

面向字节流,读写数据的时候,是以字节为单位的

面向数据报,读写数据的时候,是以一个数据报为单位(不是字符为单位),一次必须读写一个 UDP 数据报,不能是半个。

五、全双公 vs 半双工

全双工:一个通信链路,支持 双向通信 (能读,也能写)

半双工:一个通信链路,只支持单向通信(只能读,或者只能写)

他们俩就像我们的一条公路,公路上的黄色线,就是车道的分割线。全双工就像是双向公路,这条路可以从A到B,也可以从B到A。半双工就像是单向公路,只能从A到B,或者是从B到A。

接下来就可以使用socket api 进行网络编程了

在计算机中"文件 "通常是一个"广义的概念 ",文件还能代指一些硬件设备(操作系统管理硬件设备,也是抽象成文件,统一管理的)。 就像 网卡 抽象成 socket 文件,操作网卡的时候,流程和操作普通文件差不多, 打开 -> 读写 -> 关闭。

操作网卡,直接不好操作,把操作网卡当成操作 socket 文件,就容易操作了。socket文件就相当于"网卡的遥控器"。

六、回升服务器(重点)

下面我们写一个回声服务器。回声服务器是一个客户端输入什么,服务器端什么任务都不做,直接返回客户端输入的内容

先说一下大致流程:

  1. 创建一个接口 ,这个接口的端口必须是固定 的,为的就是防止这个服务器今天在这个端口,明天在另外一个端口,端口非常随机,我们很难去访问这个流动的服务器,所以我们把端口给固定之后,端口就不会跑了。这是类的创建和构造函数。Datagram 是数据报,Socket 是接口,所以 DatagramSocket 是数据报的接口,创建一个数据报的接口。

    java 复制代码
    public class UdpEchoServer01 {
        private DatagramSocket socket = null;
    
        public UdpEchoServer01(int port) throws SocketException {
            // 指定一个固定端口号,给服务器使用
            socket = new DatagramSocket(port);
        }
    
    }
  2. 创建好接口之后,因为这个接口是给服务器服务的,所以我们需要不断的接收客户端的请求。

    java 复制代码
     public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
            }
    }
  3. 在不断请求中,我们需要去获取客户端的请求,所以我们需要创建一个数据报,来接收请求的内容

    java 复制代码
     public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            }
    }
  4. 上面我们创建好这个数据报之后,去调用接口来给这个数据报读取客户端发来的请求 。就能知道客户端要做什么了,如果客户端没有请求的发送,这里也会发生阻塞等待
    我们需要一个 byte 数据来保存请求的数据,所以创建这个请求数据报的时候需要传入一个byte 数组来保存请求。

    java 复制代码
     public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
    ////////////    使用接口给 requestPacket 这个数据报中传入客户端的请求
                socket.receive(requestPacket);
    
            }
    }
  5. 经过 requestPacket 这个请求数据报接收客户端发送的请求之后,这个数据报内就保存好了客户端发送的数据报的内容 ,我们把其中的客户端发送的请求给解析出来放到一个String中。
    因为是构造String ,所以我们获取到data 之后,需要从0开始读取然后构造这个数据报的长度的大小。

    java 复制代码
     public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
                socket.receive(requestPacket);
    ////////////////////////   获取 requestPacket 中的 data 
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
    
    //////////////   这里无法使用,如果传入 getData().length 每次构造的长度都会是4096,会造成请求混乱
                 //  String request = new String(requestPacket.getData(), 0, requestPacket.getData().length);
            }
    }
  6. 我们有了这个请求之后,构造一个处理函数,处理一下这个请求, 模拟一下真实服务器的运行。因为这里我们写的是回声服务器,你也可以直接使用这个请求字符串来构造响应数据报。这里封装成一个函数来处理这个请求。

    java 复制代码
        public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
                socket.receive(requestPacket);
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
    ////       使用 response 接收响应
                String response = process(request);
    
            }
        }
    ///// 服务器处理请求返回响应的函数
        public String process(String request){
            return request;
        }
  7. 拿到响应之后我们就可以构造数据报返回给客户端的接口, 这时候我们需要知道客户端的IP和端口 ,这两个内容都在requestPacket接收客户端的请求的时候就保存起来了, 这时候我们可以构造一个响应数据报 ,直接调用 requestPacket 里面的getAddress() 获取 IP,getPort() 获取端口。或者使用 getSocketAdderss() 直接获取客户端的 IP和端口。

    java 复制代码
        public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
                socket.receive(requestPacket);
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
                String response = process(request);
    
    ///////////////// 直接使用 getSocketAdderss()
                DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                        requestPacket.getSocketAddress());
    /////////////// 使用 getAddress() 和 gerPort() 分别获取 IP 和 端口号
    //            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
    //                    requestPacket.getAddress(), requestPacket.getPort());
    
            }
        }
        public String process(String request){
            return request;
        }
  8. 最后可以把响应数据报返回给客户端,之后就是客户端来接受这个响应了。
    还可以打印一下服务器端日志,requestPacket 里面保存了客户端的 IP端口号
    IP:端口号 req: 请求 resp: 响应
    哪个 IP 的 机器,哪个端口的程序 访问了服务器,请求是什么,服务器给的响应是什么

    java 复制代码
        public void start() throws IOException {
            System.out.println("服务器启动");
            while(true){
                DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
                socket.receive(requestPacket);
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
                String response = process(request);
                DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                        requestPacket.getSocketAddress());
    //////////   把响应数据报返回给客户端
                socket.send(responsePacket);
    /////////////  打印一下服务器日志
                System.out.printf("[%s:%d] req: %s resp: %s\n", requestPacket.getAddress(), requestPacket.getPort(),
                                    request, response);
            }
        }
        public String process(String request){
            return request;
        }
相关推荐
砚凝霜1 小时前
软考网络工程师|第 2 章 信道延迟、传输介质、数据编码、数字调制、PCM 完整备考笔记
网络·笔记·pcm
aixingkong9211 小时前
Atlas 950 1024卡SuperPoD推测分析
网络·人工智能·硬件架构·硬件工程
码农学院4 小时前
基于运维监控体系的网络品牌推广方案:从架构设计到技术实现
运维·网络
REDcker11 小时前
显示分辨率标准对照详解
前端·网络·分辨率·显示·屏幕
ANSIOT13 小时前
安朔科技蓝牙无感开锁方案,靠近自动解锁
网络·科技·物联网
晏宁科技YaningAI13 小时前
VoIP系统的工程实现模型:从信令控制到媒体传输的完整架构解析
网络·人工智能·架构·系统架构·信息与通信
神奇霸王龙16 小时前
Claude Code屠榜:MiMo与Grok紧追Codex
服务器·网络·人工智能·gpt·ai·ai编程
数智化管理手记18 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发18 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序