回显服务器(基于UDP)

目录

基本概念

API学习

DatagramSocket

DatagramPacket

InetSocketAddress

回显服务器实现

服务端

思路分析

具体实现

完整代码

客户端

思路分析

具体实现 

完整代码

运行测试


基本概念

发送端和接收端

在一次网络数据传输时:

发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机

接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机

发送端和接收端是相对的

请求和响应

一般来说,获取一个网络资源,涉及到两次网络数据传输:

第一次:请求数据的发送

第二次:响应数据的发送

服务端和客户端

服务端:在常见的网络数据传输场景下,将提供服务的一方进程,称为服务端,可以提供对外服务

客户端:获取服务的一方进程,称为客户端

对于服务来说,一般是提供:

客户端获取服务资源:

        客户端保存资源在服务端:

常见的客户端服务端模型:

最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

(1)客户端先发送请求到服务端

(2)服务端根据请求数据,执行相应的业务处理

(3)服务端返回响应:发送业务处理结果

(4)客户端根据响应数据,展示处理结果(展示获取的资源,或是提示保存资源的处理结果)

API学习

DatagramSocket

DatagramSocket是UDP Socket,用于发送和接收UDP数据报

构造方法:

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

常用方法:

方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据包(不会阻塞等待,直接发送)
void close关闭此数据报套接字

DatagramPacket

DatagramPacket是UDP Socket发送和接收的数据报

构造方法:

方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket用来接收数据报,接收的数据保存在字节数组(buf)中,接收指定长度(length)
DatagramPacket(byte[] buf, int length, SocketAddress address)构造一个DatagramPacket用来发送数据报,发送的数据在字节数组(buf),发送指定长度(length),address指定目的主机的IP地址和端口号
DatagramPacket(byte[] buf, int int length, InentAddress, int port)构造一个DatagramPacket用来发送数据报,发送的数据保存在字节数组(buf),发送指定长度(length)到address主机的port端口

 常用方法:

方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址和端口号;或从发送的数据报中,获取接收端主机IP地址和端口号
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

构造UDP发送的数据报时,需要传入 SocketAddress,该对象可以使用InetSocketAddress来创建

InetSocketAddress

InetSocketAddress(SocketAddress的子类)构造方法:

方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

回显服务器实现

回显服务器的业务逻辑较为简单,通过编写回显服务器,我们能够进一步熟悉相关API和编写流程

服务器接收客户端的请求,返回响应,响应的内容是客户端发送的请求内容(即客户端发什么,服务端响应什么)

其中,涉及到客户端和服务端,我们先实现服务端相关逻辑

服务端

思路分析

对于服务器,要实现的内容有:

1. 接收客户端发送的请求

2. 读取解析请求,并根据请求计算响应

3. 将响应返回给客户端

在实现服务器过程中我们需要注意的是,由于服务器要等到客户端发送请求时才能进行接收、解析、计算响应等操作,而服务器不知道客户端什么时候发送请求,因此服务器需要一直“待命”,等待客户端发送请求

具体实现

首先我们需要创建一个 DatagramSocket 对象用于接收UDP数据报,并通过构造方法来指定服务器要绑定的端口号

import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    DatagramSocket socket = null; //创建 DatagramSocket 对象用于发送和接收UDP数据报
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
}

接下来,我们实现服务器的启动,接收客户端发送的请求并解析

    //启动服务器
    public void start() throws IOException {
        System.out.println("启动服务器");
        while (true) {//服务器需要长期、返回地接收请求、处理请求
            // 接收请求并解析
            //创建 DatagramPacket 对象用于接收客户端发送的数据报
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //接收请求数据报
            socket.receive(requestPacket);
            //将读取到的字节数组转换成String 方便后续逻辑处理
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            
        }
    }

其中的receive()能够读取到一个UDP数据报,并将其放到requestPacket中,其中,UDP数据报的载荷部分被放到requestPacket内置的字节数组中,而报头部分,则会被requestPacket的其他属性保存。在执行到receive()时,若还没有客户端发来请求,则会先阻塞等待,直到接收到客户端发送的请求

接下来,根据请求计算响应,对于回显服务器,这一步非常简单,只需要将请求内容直接作为响应内容即可

    //启动服务器
    public void start() throws IOException {
        System.out.println("启动服务器");
        while (true) {//服务器需要长期、反复地接收请求、处理请求
           // 接收请求并解析
            //创建 DatagramPacket 对象用于接收客户端发送的数据报
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //接收请求数据报
            socket.receive(requestPacket);
            //将读取到的字节数组转换成string 方便后续逻辑处理
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

            //根据请求计算响应
            String response = process(request);
        }
    }
    public String process(String request){
        return request;
    }

基于字节数组构造出String,字节数组中保存的内容不一定是二进制数据,也可能是文件数据,而String既可以保存文本数据也可以保存二进制数据

最后,我们将响应返回到客户端

            //将响应返回给客户端
            //构造 DatagramPacket 对象用于存储向客户端发送响应内容
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress()); //此时需要传入发送数据的字节数组、数据长度和发送请求的客户端的IP地址和端口号
            //发送响应数据报
            socket.send(requestPacket);

 在发送请求时,将请求中的源IP和源端口号,作为响应的目的IP和目的端口号,此时就能够将消息返回给客户端

为了方便观察程序的执行效果,我们可以打印日志

完整代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    DatagramSocket socket = null;//创建 DatagramSocket 对象用于发送和接收UDP数据报
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    //启动服务器
    public void start() throws IOException {
        System.out.println("启动服务器");
        while (true) {//服务器需要长期、反复地接收请求、处理请求
            // 接收请求并解析
            //创建 DatagramPacket 对象用于接收客户端发送的数据报
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //接收请求数据报
            socket.receive(requestPacket);
            //将读取到的字节数组转换成string 方便后续逻辑处理
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

            //根据请求计算响应
            String response = process(request);

            //将响应返回给客户端
            //构造 DatagramPacket 对象用于存储向客户端发送响应内容
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress()); //此时需要传入发送数据的字节数组、数据长度和发送请求的客户端的IP地址和端口号
            //发送响应数据报
            socket.send(requestPacket);
            //打印日志
            System.out.printf("[%s:%d] req: %s, resp: %s\n", responsePacket.getAddress().toString(), responsePacket.getPort(),
                    request, response);
        }
    }
    public String process(String request){
        return request;
    }
}

接下来,我们运行服务器:

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

由于服务器要等待客户端发送请求后才能执行后续操作,因此,我们先实现客户端后再来观察效果

实现服务器后,我们接着实现客户端相关操作

客户端

思路分析

对于服务端要实现的内容有:

1. 从控制台读取用户输入的内容

2. 将内容构造成UDP请求,并发送给服务器

3. 等待服务器响应,当接收到服务器响应时,解析响应内容

4. 将响应内容显示到屏幕上

具体实现 

首先,我们要创建一个 DatagramSocket 对象用于接收UDP数据报,但此时不需要手动指定端口号

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;//服务器IP
    private int serverPort;//服务器端口号
    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();
    }
}

由于我们要知道服务器的IP地址和端口才能向其发送请求,因此,在这里我们通过构造方法来指定服务器的IP和端口号 

为什么服务器需要指定端口号,而客户端则是不需要?

 对于服务器,需要手动指定端口,对于客户端,则一般不用手动指定,此时系统会自动分配一个空闲的端口号。这是因为对于客户端来说,无法保证手动指定的端口是可用的,因此由系统随机分配一个空闲的端口号,而对于服务器端口来说,我们事先知道服务器上有哪些端口被使用,因此我们可以确保指定的端口未被占用,此外,客户端要主动给服务器发起请求,通过服务器端口号找到服务器,若服务器端口号也随机分配,则客户端不能找到服务器

接下来我们启动服务器,并从控制台读取要发送的请求数据

    //启动服务器
    public void start(){
        System.out.println("启动客户端");
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.print("请输入:");
            if(!scanner.hasNext()){
               break; 
            }
            String request = scanner.next();
            
        }
    }

接下来,我们由输入的内容构造请求并发送

    //启动服务器
    public void start() throws IOException {
        System.out.println("启动客户端");
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.print("请输入:");
            if(!scanner.hasNext()){
               break;
            }
            String request = scanner.next();
            //构造请求并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
        }
    }

由于数据报需要进行发送,因此需要有目的主机的IP地址和端口号,而构造方法中传入的IP地址是字符串类型,且是点分十进制风格的(例如“127.0.0.1”),因此我们需要使用 InetAddress.getByName 创建InetAddress对象

然后我们需要读取服务器返回的响应并显示

            //读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            //将响应显示到控制台上
            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", 9010);
        client.start();
    }

完整代码

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;//服务器IP
    private 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){
            System.out.print("请输入:");
            if(!scanner.hasNext()){
               break;
            }
            String request = scanner.next();
            //构造请求并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
            //读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            //将响应显示到控制台上
            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", 9010);
        client.start();
    }
}

运行测试

此时,我们同时运行服务器和客户端,并输入请求观察代码是否存在问题:

客户端:

服务器:

 此时我们看到,客户端输入的请求后响应成功显示,且服务器记录日志正确

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

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

相关文章

Linux中的软链接与硬链接

Linux链接概念 Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link)。默认情况下,使用 ln 命令不加参数创建硬链接,加 -s 参数则创建软链接 硬链接…

2024不可不会的StableDiffusion之扩散模型(四)

1. 引言 这是我关于StableDiffusion学习系列的第四篇文章,如果之前的文章你还没有阅读,强烈推荐大家翻看前篇内容。在本文中,我们将学习构成StableDiffusion的第三个基础组件基于Unet的扩散模型,并针该组件的功能进行详细的阐述。…

RK3568平台开发系列讲解(Linux系统篇)platform 设备的注册

🚀返回专栏总目录 文章目录 一、platform_device_register 注册函数二、platform_device_unregister 反注册函数三、platform_device 结构体四、resource 结构体沉淀、分享、成长,让自己和他人都能有所收获!😄 一、platform_device_register 注册函数 platform_device_re…

海外云手机运营Instagram攻略

Instagram是世界著名的社交媒体平台,有着10亿实时用户,是跨境电子商务的优质流量来源。平台以女性用户为主,购物倾向高,转化率好。它被公认为外贸行业的优质社交媒体流量池。那么,如何使用海外云手机吸引Instagram上的…

【论文阅读】Long-Tailed Recognition via Weight Balancing(CVPR2022)

目录 论文使用方法weight decayMaxNorm 如果使用原来的代码报错的可以看下面这个 论文 问题:真实世界中普遍存在长尾识别问题,朴素训练产生的模型在更高准确率方面偏向于普通类,导致稀有的类别准确率偏低。 key:解决LTR的关键是平衡各方面&a…

力扣题集(第一弹)

一日练,一日功;一日不练十日空。 学编程离不开刷题,接下来让我们来看几个力扣上的题目。 1. 242. 有效的字母异位词 题目描述 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数…

JS图片二维码识别

前言 js识别QR图片&#xff0c;基于jsQR.js 代码 <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title>图片二维码识别</title><script src"https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js">…

什么是消息队列?

消息用队列的模式发送&#xff0c; 把要传输的数据放在队列中&#xff0c; 产生消息的叫做生产者&#xff0c; 从队列里取出消息的叫做消费者。 一、组成 生产者&#xff1a;Producer 消息的产生者与调用端 主要负责消息所承载的业务信息的实例化 是一个队列的发起方 代理…

网站小程序分类目录网源码系统+会员注册登录功能 附带完整的搭建教程

随着互联网的发展&#xff0c;小程序分类目录网站已经成为了人们获取各类信息的重要渠道。而在这个领域中&#xff0c;罗峰给大家分享一款网站小程序分类目录网源码系统以其强大的功能和易用性&#xff0c;脱颖而出。本系统集成了会员注册登录功能&#xff0c;让用户能够更加便…

uniapp H5 实现上拉刷新 以及 下拉加载

uniapp H5 实现上拉刷新 以及 下拉加载 1. 先上图 下拉加载 2. 上代码 <script>import DragableList from "/components/dragable-list/dragable-list.vue";import {FridApi} from /api/warn.jsexport default {data() {return {tableList: [],loadingHi…

Redis核心技术与实战【学习笔记】 - 6.Redis 的统计操作处理

1.前言 在 Web 业务场景中&#xff0c;我们经常保存这样一种信息&#xff1a;一个 key 对应了一个数据集合。比如&#xff1a; 手机 APP 中的每天用户登录信息&#xff1a;一天对应一系列用户 ID。电商网站上商品的用户评论列表&#xff1a;一个商品对应了一些列的评论。用户…

12 数据仓库理论

数仓基本概述 数据仓库基本概念 数据仓库是一个为数据分析而设计的企业级数据管理系统。数据仓库可集中 、整合多个信息源的大量数据。 数仓核心架构 数据仓库建模概述 数据仓库建模意义 数据模型就是数据组织和存储方法&#xff0c;它强调从业务、数据存取和使用角度合理…

Django配置websocket时的错误解决

基于移动群智感知的网络图谱构建系统需要手机app不断上传数据到服务器并把数据推到前端标记在百度地图上&#xff0c;由于众多手机向同一服务器发送数据&#xff0c;如果使用长轮询&#xff0c;则实时性差、延迟高且服务器的负载过大&#xff0c;而使用websocket则有更好的性能…

链表与二叉树-数据结构

链表与二叉树-数据结构 创建叶子node节点建立二叉树三元组&#xff1a;只考虑稀疏矩阵中非0的元素&#xff0c;并且存储到一个类&#xff08;三元组&#xff09;的数组中。 创建叶子node节点 class Node{int no;Node next;public Node(int no){this.nono;} } public class Lb…

YOLOv8改进 | 可视化热力图 | 支持YOLOv8最新版本密度热力图,和视频热力图

一、本文介绍 本文给大家带来的机制是集成了YOLOv8最新版本的可视化热力图功能,热力图作为我们论文当中的必备一环,可以展示出我们呈现机制的有效性,本文的内容支持YOLOv8最新版本的根据密度呈现的热力图,同时支持视频检测,根据视频中的密度来绘画热力图。 在开始之前给…

薅运营商羊毛?封杀!

最近边小缘在蓝点网上看到一则消息 “浙江联通也开始严格排查PCDN和PT等大流量行为 被检测到可能会封停宽带”。 此前中国联通已经在四川和上海等多个省市严查家庭宽带 (部分企业宽带也被查) 使用 PCDN 或 PT&#xff0c;当用户的宽带账户存在大量上传数据的情况&#xff0c;中…

数据库管理-第141期 DG PDB - Oracle DB 23c(20240129)

数据库管理141期 2024-01-29 第141期 DG PDB - Oracle DB 23c&#xff08;20240129&#xff09;1 概念2 环境说明3 操作3.1 数据库配置3.2 配置tnsname3.3 配置强制日志3.4 DG配置3.5 DG配置建立联系3.6 启用所有DG配置3.7 启用DG PDB3.8 创建源PDB的DG配置3.9 拷贝pdbprod1文件…

【C++】I/O多路转接详解(一)

目录 1. 背景引入1.1 IO的过程1.2 五种IO模型1.2.1 阻塞IO1.2.2 非阻塞IO1.2.3 信号驱动IO1.2.4 IO多路转接1.2.5 异步IO 1.3 同步通信 与 异步通信1.4 阻塞 与 非阻塞1.4.1 阻塞与非阻塞区别1.4.2 设置非阻塞IO 2. select2.1 接口使用2.2 select执行过程2.3 select代码实践 3.…

<网络安全>《9 入侵防御系统IPS》

1 概念 IPS&#xff08; Intrusion Prevention System&#xff09;是电脑网络安全设施&#xff0c;是对防病毒软件&#xff08;Antivirus Programs&#xff09;和防火墙&#xff08;Packet Filter, Application Gateway&#xff09;的补充。 入侵预防系统&#xff08;Intrusio…

JS第一课简单看看这是啥东西

1.什么是JavaScript JS是一门编程语言&#xff0c;是一种运行在客户端(浏览器)的编程语言&#xff0c;主要是让前端的画面动起来&#xff0c;注意HTML和CSS不是编程语言&#xff0c;他俩是一种标记语言。JS只要有浏览器就能运行不用跟Python或者Java一样上来装一个jdk或者Pyth…