「网络编程」基于 UDP 协议实现回显服务器

🎇个人主页:Ice_Sugar_7
🎇所属专栏:计网
🎇欢迎点赞收藏加关注哦!

实现回显服务器

  • 🍉socket api
  • 🍉回显服务器
    • 🍌实现
      • 🥝服务器
      • 🥝客户端

🍉socket api

操作系统给我们提供的进行网络编程的 api 称为 socket api(网络编程套接字),具体到传输层,有两个重要的协议的 api —— UDP apiTCP api,本文我们介绍的是 UDP api

UDP 有四个特点:无连接、不可靠传输、面向数据报、全双工。这在后文中会解释

Java 对系统原生的 api 进行了封装,UDP socket 有两个核心的类

  1. DatagramSocket

操作系统中有一类文件,叫作 socket 文件,它和我们之前所说的“文件”不太一样,我们平时所说的普通文件、目录文件位于硬盘上,而 socket 文件则是抽象表示了网卡这样的硬件设备(网卡是网络通信中的核心硬件设备),也就是把网卡等硬件视为一种文件。通过网卡发送数据就是写 socket 文件;接收数据就是读 socket 文件
说回 DatagramSocket,它负责读写 socket 文件,也就是借助网卡发送或接收数据

它有两个构造方法:

构造方法说明
DatagramSocket()创建一个 UDP 数据报套接字的 Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个 UDP 数据报套接字的 Socket,绑定到本机指定的端口,即 port(一般用于服务器)

负责发送和接收的方法如下:

方法说明
void reveive(DatagramPacket p)让 p 接收数据报(如果没接收到数据报,这个方法就会阻塞等待)(注意这里的参数是输出型参数,实际上 DatagramPacket 内部包含了一个字节数组
void send(DatagramPacket p)从 p 发送数据报(直接发送出去,不会阻塞)

  1. DatagramPacket

DatagramPacket 表示一个 UDP 数据报。UDP 面向数据报,每次发送、接收数据的基本单位就是一个 UDP 数据报


🍉回显服务器

这是网络编程中最简单的程序,相当于 hello world,不过还是有一定的难度
服务器在接收客户端的请求后会返回响应,具体返回什么响应,要根据实际的业务场景分析。对于回显服务器,它没有业务逻辑,客户端发什么请求,服务器就返回什么响应

🍌实现

接下来我们通过 UDP 协议来实现一个回显服务器

🥝服务器

首先要创建一个 DatagramSocket 对象,然后要通过这个 socket 对象来操作网卡

public class UdpEchoServer {
    DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port); //在运行一个服务器程序时,通常会手动指定端口
    }
}

补充:这里的 SocketException 是网络编程中一个常见的异常,通常表示 socket 创建失败,比如端口号已经被别的进程占用了

接下来服务器主要做三件事

①读取请求并解析
②根据请求计算响应。对于回显服务器来说,这一步啥都不用做
③把响应返回到客户端

要读取请求得先创建一个 DatagramPacket 接收请求

DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());

在这里插入图片描述
使用字节数组构造字符串的方法一定要记住

在这里插入图片描述
调用 receive 涉及到缓冲区,下面通过图示补充一下:

在这里插入图片描述
第二步是根据请求计算响应,虽然回显服务器这一步不用做什么,不过为了逻辑完整,我们写一个 process 方法,它只返回 request
(如果是具有特定业务的服务器,process 中就写其他你想要的逻辑)

public String process(String request) {
    return request;
}

最后就是把响应返回给客户端,这一步要用到 send 方法

//3.把响应返回到客户端
//构造一个 DatagramPacket 作为响应对象
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,
        response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);

在这里插入图片描述
接下来在主方法中启动服务器

在这里插入图片描述
服务器的代码如下:

public class UdpEchoServer {
    DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    //服务器的启动逻辑
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true) {
            //每次循环就是处理一个请求,然后返回响应的过程
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            //1.读取请求并解析
            socket.receive(requestPacket);
            //填充字节数组后,将其转为 String 方便后续处理逻辑
            //getData 方法获取到 DatagramPacket 内部的字节数组
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.根据请求计算响应
            String response = process(request);
            //3.把响应返回到客户端
            //构造一个 DatagramPacket 作为响应对象
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,
                    response.getBytes().length,requestPacket.getSocketAddress());
            socket.send(responsePacket);
            
            //打印日志
            System.out.printf("[%s:%d] req:%s, resp:%s\n",requestPacket.getAddress().toString(),
        requestPacket.getPort(),request,response);
        }
    }

    public String process(String request) {
        return request;
    }
    
    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(7000);
        server.start();
    }
}

🥝客户端

接下来编写客户端的代码
首先要创建 socket 对象,注意客户端这里不需要手动指定端口号

在这里插入图片描述

1.在代码中手动指定端口号,可以保证端口号始终固定;如果不手动指定,那就是系统自动分配,这样的话服务器每次重启之后端口号可能就变了,一旦变了,客户端就可能找不到服务器在哪儿了,所以服务器需要手动指定
2.而对于客户端,因为无法确保手动指定的端口是可用的(可能被其他进程占用了),这就可能导致程序因为端口绑定失败而无法启动,所以让系统随机分配一个空闲的端口就 ok 了

接下来客户端要做四件事

1.从控制台读取要发送的请求数据
2.构造请求并发送
3.读取服务器的响应
4.把响应显示到控制台上

第一步就是先创建一个 Scanner 对象来读取字符串
这里补充一点,使用 Scanner 从控制台读取字符串的话最好使用 next,因为如果用 nextLine 读取需要手动输入换行符 enter,但是 enter 键除了产生 \n 还会产生其他字符,这就会导致读取到的内容容易出问题;而如果从文件读取的话那就用哪个都行

第二步构造请求就用接收的字符串来构造一个 DatagramPacket 对象,然后发送

DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
        InetAddress.getByName(serverIp),serverPort);
socket.send(requestPacket);

到这里我们已经了解了三种构造 DatagramPacket 对象的方法,总结一下:

//第一种:搭配 receive 使用。构造的时候指定空白的字节数组
DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);

//第二种:发数据时使用。构造时指定有内容的字节数组,并指定 IP 和端口
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());

//第三种:发数据时使用。构造时指定有内容的字节数组,并指定 IP 和 端口,这两者分开指定
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIp),serverPort);

回到正题,第三步是读取服务器响应

DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
socket.receive(responsePacket);

最后就是把它显示到控制台:

String response = new String(responsePacket.getData(),0,responsePacket.getLength()); //再次强调,这里的 getLength 方法得到的是有效长度
System.out.println(response);

客户端代码如下:

public class UdpEchoClient {
    DatagramSocket socket;
    String serverIp;
    int serverPort;
    public UdpEchoClient(String serverIp,int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();
    }

    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        while(true) {
            if(!scanner.hasNext()) break;
            //1. 从控制台读取要发送的请求数据
            String request = scanner.next();
            //2.构造请求并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIp),serverPort);
            socket.send(requestPacket);
            //3.读取服务器的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(responsePacket);
            //4.把响应显示到控制台上
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",7000);
        client.start();
    }
}

接下来运行一下看看效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/679273.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

插入mysql报错:Incorrect string value: ‘\xF0\xAC\x8C\x97\xE5\x9E...‘

原因分析 这个错误通常发生在使用MySQL数据库时,尝试将包含四字节UTF-8字符(通常表示为Unicode码点大于UFFFF的字符)插入到一个不支持这种字符的字符集列中。一般在插入睡眠emoji表情时容易遇到 解决 -- 设置数据库编码utf8mb4 ALTER DAT…

伦敦金当前行情你真的看懂了吗?

5月中旬,伦敦金价将历史新高再次改写至2450美元/盎司,虽然随后两周出现了反复回落的走势,但整体的升浪仍然受到50天指数移动平均线的支撑。有分析机构预计,随着美联储美联储开始放缓缩表和开启降息周期,来年的伦敦金价…

Spring Boot自动配置原理和应用

我们知道,基于Spring Boot,我们只需要在类路径中引入一组第三方框架的starter组件,就能在Spring容器中使用这些框架所提供的各项功能。这在当下的开发过程中已经习以为常,但在Spring Boot还没有诞生之前却是不可想象的。如果我们使…

【Text2SQL 论文】QDecomp:探索 CoT-style 的 prompt 来解决 Text2SQL

论文:Exploring Chain of Thought Style Prompting for Text-to-SQL ⭐⭐⭐⭐ EMNLP 2023, arXiv:2305.14215 一、论文速读 本文通过对 LLM 使用 CoT-style 的 prompting 方法来解决 Text2SQL 问题,试图回答下面两个问题: 哪种 prompting s…

英伟达GPU架构加速狂飙

NVIDIA首席执行官黄仁勋在台湾大学体育馆发表主题演讲,展示了新一代Rubin架构,这是NVIDIA加速推出新架构的最新成果。 在讨论NVIDIA下一代架构时,黄仁勋提到了Blackwell Ultra GPU,并表示它可能会继续升级。然后他透露&#xff0c…

Zoom | saas企业分销裂变的典范

提到视频通讯,相信大家不会陌生,国外有Skype、Google meeting、Facetime,国内有腾讯会议、钉钉,为什么在如此众多竞争对手的情况下,Zoom能够一马当先,成为行业先锋? 一、公司简介 Zoom是集视频…

【电路笔记】-Sallen-Key滤波器

Sallen-Key滤波器 Sallen-Key 滤波器拓扑用作实现高阶有源滤波器的构建块。 1、概述 Sallen-Key 滤波器设计是一种二阶有源滤波器拓扑,我们可以将其用作实现高阶滤波器电路的基本构建块,例如低通 (LPF)、高通 (HPF) 和带通 ( BPF)滤波器电路。 正如我们在本滤波器部分中…

反激电源的类型与特点

主要分为 1 固定频率(CCMDCM) 2 可变频率控制(CRM电流临界模式) 这三种模式是很好辨别的,首先我们看左边的连续模式,Vds能看到他有一些尖峰毛刺,这是场效应管关闭的时候,LRC谐振导…

揭秘FL Studio21.2.8中文版一键解锁音乐创作新境界!

在音乐制作的广阔天地里,随着技术的不断进步和数字音频工作站(DAW)软件的普及,越来越多的音乐爱好者和专业制作人开始涉足音乐创作的奇妙旅程。其中,FL Studio以其强大的功能、直观的操作界面和丰富的音色资源&#xf…

用户管理的小demo--登录校检

目录 在user里面 装session 1、 LoginServlet.java 2、LoginFilter.java 3、配置路径 结果: 在user里面 装session 1、 LoginServlet.java package com.by.servlet;import com.by.pojo.User; import com.by.service.UserService; import com.by.service.impl…

云原生环境下GPU算力调度发展分析

云原生环境下GPU算力调度深度分析 概述: 云原生时代,GPU算力调度与管理备受瞩目,成为企业和云服务提供商关注的焦点,助力AI、深度学习、高性能计算等领域,满足对GPU资源的迫切需求。 容器化与编排: Kube…

LLM的基础模型4:初识Embeddings

大模型技术论文不断,每个月总会新增上千篇。本专栏精选论文重点解读,主题还是围绕着行业实践和工程量产。若在某个环节出现卡点,可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技(Mamba,xLSTM,KAN)则提…

2024.5.30

思维导图 代码 #include <iostream>using namespace std; class Rect { private:int width;int height; public:void init(int w,int h){width w;height h;}void set_w(int w){width w;}void set_h(int h){height h;}void show(){int zhou (width height) * 2;int…

vue-router 源码分析——2. router-link 组件是如何实现导航的

这是对vue-router 3 版本的源码分析。 本次分析会按以下方法进行&#xff1a; 按官网的使用文档顺序&#xff0c;围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码&#xff0c;更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升&#xff0c;甚至面试时…

使用raise语句抛出异常

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 如果某个函数或方法可能会产生异常&#xff0c;但不想在当前函数或方法中处理这个异常&#xff0c;则可以使用raise语句在函数或方法中抛出异常。rai…

车联网安全之GPS欺骗

随着车辆GPS技术的广泛应用&#xff0c;其在导航、交通监控等方面的关键性日益凸显&#xff0c;与之相关的安全性问题也备受关注。GPS欺骗便是其中一种重要的威胁手段。 本文旨在使用模拟GPS欺骗设备&#xff0c;模拟不同的GPS攻击以评估其对车辆定位系统的影响。通过这个实验…

「不只是框架:Django REST framework的超能力大揭秘」

想要让你的API服务像五星级餐厅一样令人难忘吗&#xff1f;今天阿佑将为你揭晓&#xff01;从基础的RESTful原则到Django REST framework的高级特性&#xff0c;我们一步步带你走进API开发的后厨&#xff0c;展示如何准备食材&#xff08;数据模型&#xff09;、调制酱料&#…

记录一次云服务器无法连接的排查过程

运行环境&#xff1a;阿里云服务器 故障外显原因&#xff1a;登录失败,操作系统禁用了密码登录方式 控制台监控数据显示云盘读写BPS拉满了 因为之前问过线上售后&#xff0c;让安装了atop监控&#xff0c;&#xff0c;所以先打开atop日志&#xff1a; atop -r /var/log/atop…

try…except…else语句

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中&#xff0c;还有另一种异常处理结构&#xff0c;它是try…except…else语句&#xff0c;也就是在原来try…except语句的基础上再添加一个…