回显服务器(基于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;//服务器IPprivate 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;//服务器IPprivate 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();}
}

运行测试

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

客户端:

服务器:

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

还在担心报表不好做?不用怕,试试这个方法

前言 在各种业务场景中,我们经常需要生成各种报表,例如学校中的学生成绩表、商业场景中的销售单和发票单、测量检测场景中的检测报告等等。这些报表对于组织和管理数据非常重要,因为它们提供了直观、清晰的方式来展示和分析数据。 一般情况…

【习题】保存应用数据

判断题 1. 首选项是关系型数据库。 错误(False) 2. 应用中涉及到Student信息,如包含姓名,性别,年龄,身高等信息可以用首选项来存储。 错误(False) 3. 同一应用或进程中每个文件仅存在一个Preferences实例。 正确(True) 单选题 …

西瓜书学习笔记——密度聚类(公式推导+举例应用)

文章目录 算法介绍实验分析 算法介绍 密度聚类是一种无监督学习的聚类方法,其目标是根据数据点的密度分布将它们分组成不同的簇。与传统的基于距离的聚类方法(如K均值)不同,密度聚类方法不需要预先指定簇的数量,而是通…

【-快速录用】2024年大数据经济与社会文化国际学术会议(ICBDESC 2024)

【-快速录用】2024年大数据经济与社会文化国际学术会议(ICBDESC 2024) 2024 International Conference Big Data Economy and Social Culture 一、【会议简介】 随着大数据技术的飞速发展,全球范围内对大数据经济与社会文化的研究愈发深入。为了促进国际间学术交流…

【学网攻】 第(15)节 -- 标准ACL访问控制列表

系列文章目录 目录 系列文章目录 文章目录 前言 一、ACL(访问控制列表)是什么? 二、实验 1.引入 实验拓扑图 实验配置 测试PC2能否Ping通PC3 配置ACL访问控制 实验验证 PC1 Ping PC3 总结 文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认…

java OA办公自动化系统

java OA办公自动化系统,java项目,springboot项目。eclipse和idea都能打开运行。 前端技术:Bootstrap,Jquery,My97 DatePicker,kindeditor,freemarker 后端技术:SpringBoot&#xf…

MacOS安装反编译工具JD-GUI以及解决无法打开的问题

目录 一.下载地址 二.安装 三.问题 四.解决办法 1.显示包内容 2.找到Contents/MacOS/universalJavaApplicationStub.sh 3.修改sh文件 4.保存后再次打开即可 一.下载地址 Java Decompiler 二.安装 将下载下来的 jd-gui-osx-1.6.6.tar 解压,然后将 JD-GUI.a…

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

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

最新国内GPT4.0使用入门到精通,Midjourney绘画,GPT语音对话使用,DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画,文档对话总结DALL-E3文生图,相信对大家应该不感到陌生吧?简单来说,GPT-4技术比之前的GPT-3.5相对来说更加智能,会根据用户的要求生成多种内容甚至也可以和…

【C/C++ 02】希尔排序

希尔排序虽然是直接插入排序的升级版本,和插入排序有着相同的特性,即原始数组有序度越高则算法的时间复杂度越低(预排序机制),但是是不稳定排序算法。 为了降低算法的时间复杂度,所以我们需要在排序之前尽…

《HTML 简易速速上手小册》第9章:HTML5 新特性(2024 最新版)

文章目录 9.1 HTML5 新增标签和属性9.1.1 基础知识9.1.2 案例 1:创建一个结构化的博客页面9.1.3 案例 2:使用新的表单元素创建事件注册表单9.1.4 案例 3:创建一个具有高级搜索功能的搜索表单 9.2 HTML5 表单增强9.2.1 基础知识9.2.2 案例 1&a…

Windows Server 2025 LTSC 预览版来了

Windows Server 2025 LTSC 预览版来了 1. 安装 Windows Server 2025 LTSC 预览版2. 安装 VMware Tools3. Windows Server 2025 LTSC 预览版4. Windows Server 2025 LTSC 预览版下载地址 1. 安装 Windows Server 2025 LTSC 预览版 使用 VMware Workstation 安装, 安…