「网络编程」基于 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();}
}

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

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

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

相关文章

纯血鸿蒙实战开发—如何添加顶部tab页面

1.Tabs组件 Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏. 根据不同的导航类型,布局会有区别,可以分为底部导航、顶部导航、侧边导航,其导航栏分别位于底部、顶…

react基础学习 JSX

JSX的测试网站 Babel Babel 可以测试代码的效果 JSX实现map列表 注意 key不一样(使用遍历的时候) 简单条件渲染 复杂条件渲染 绑定事件 function App() {const colorse (e)>{console.log("测试点击",e);}const colorse1 (name)>{…

地理信息科学中的大数据挑战

在信息化爆炸的时代,地理信息科学(GIScience)正经历着前所未有的变革,其中,地理空间大数据的涌现为科学研究与应用带来了前所未有的机遇与挑战。作为地理信息与遥感领域的探索者,本文旨在深入剖析地理空间大…

揭秘HubSpot集客营销:如何吸引并转化全球潜在客户

随着全球数字化浪潮的推进,企业出海已经成为许多公司扩大市场、增加品牌曝光度的重要战略。HubSpot集客营销作为一种以客户为中心、数据驱动的营销策略,为企业在海外市场的成功提供了强有力的支持。作为HubSpot亚太地区的合作伙伴,NetFarmer将…

godot的安装和使用 1

今天是第一节,因此呢先做godot的安装,其实很简单 godot官网:https://godotengine.org/ 进入官网, 安装好之后呢,会有两个文件 打开第一个就是可视化界面的,进入后是这个样子 说明安装成功了

鸿蒙开发接口安全:【@ohos.abilityAccessCtrl (访问控制管理)】

访问控制管理 说明: 本模块首批接口从API version 8开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 导入模块 import abilityAccessCtrl from ohos.abilityAccessCtrlabilityAccessCtrl.createAtManager createAtManager(): AtMan…

OpenCV学习(4.1) 改变颜色空间

1.目标 在本教程中,你将学习如何将图像从一个色彩空间转换到另一个,像BGR↔灰色,BGR↔HSV等除此之外,我们还将创建一个应用程序,以提取视频中的彩色对象你将学习以下功能:cv2.cvtColor,**cv2.i…

更适合工程师和研究僧的FPGA专项培训课程

各位编程精英er~ 社区打造的FPGA工程师培训班上线后,有不少同学后台私信询问:“能不能出个那种专门针对某个知识点的课程呢?我想针对自己的薄弱点深入学习。” 贴心如我,当然会满足大家的学习需求啦。本周,社区FPGA专…

Nextjs使用教程

一.手动创建项目 建议看这个中文网站文档,这个里面的案例配置都是手动的,也可以往下看我这个博客一步步操作 1.在目录下执行下面命令,初始化package.json文件 npm init -y2.安装react相关包以及next包 yarn add next react react-dom // 或者 npm install --save next react…

使用Python操作Redis

大家好,在当今的互联网时代,随着数据量和用户量的爆发式增长,对于数据存储和处理的需求也日益增加。Redis作为一种高性能的键值存储数据库,以其快速的读写速度、丰富的数据结构支持和灵活的应用场景而备受青睐。本文将介绍Redis数…

猫头虎分享已解决Bug || Error: ‘fetch‘ is not defined

原创作者: 猫头虎 作者微信号: Libin9iOak 作者公众号: 猫头虎技术团队 更新日期: 2024年6月6日 博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: &…

独立游戏之路 -- 上架TapTap步骤和注意事项

个人开发者游戏上架TapTap上架步骤和注意事项 一、TapTap 介绍二、独立游戏上架 TapTap 的步骤2.1 创建游戏2.2 提交游戏审核2.3 TapTap 平台上发布。 三、注意事项3.1 关于备案3.2 遵守 TapTap 的规定3.3 保证游戏质量 四、常见问题4.1 隐私政策问题4.2 先发布还是先优化&…

PIVOT函数-动态列

一、需求说明 原始表&#xff1a; select * from pathogen_pro; 将pm_name的值转成对应的列&#xff0c;效果如下 二、PIVOT函数说明 PIVOT(<聚合函数>([聚合列值]) FOR [行转列前的列名] IN([行转列后的列名1],[行转列后的列名2],[行转列后的列名3],.......[行转列后…

Julia编程11:变量作用域 Scope of Variables

There are two main types of scopes in Julia, global* scope* and local* scope*. Julia有全局变量作用域和局部变量作用域&#xff0c;函数或者一些结构体、循环体如for等是否内部是局部环境可以参照下表。 ConstructScope typeAllowed withinmodule, baremoduleglobalglo…

.Net 基于.Net8开发的一个Asp.Net Core Webapi后端框架

1.项目结构 该项目是基于.net8开发的Asp.Net Core WebApi后端服务,集成了Efcore,Autofac,Jwt,AutoMapper,Serilog,Quartz,MiniExcel等组件。该框架简单易上手&#xff0c;没有额外的学习成本; 该项目采用了多层结构设计&#xff0c;有利于解耦&#xff0c;包含公共层&#xff0…

MySQL数据库整体知识点简述

目录 第一章&#xff1a;数据库系统概述 第二章&#xff1a;信息与数据模型 第3章 关系模型与关系规范化理论 第四章——数据库设计方法 第六-七章——MySQL存储引擎与数据库操作管理 第九章——索引 第10章——视图 第11章——MySQL存储过程与函数 第12章——MySQL 触…

IIS7整合Tomcat9服务器,并搭建ASP+PHP+JSP完整运行环境

本文以Windows Vista系统为例&#xff0c;详细讲解IIS7整合Tomcat服务器&#xff0c;同时支持ASPPHPJSP三种Web动态网页技术的方法。 Vista系统自带的IIS版本为7.0&#xff0c;能安装的IE浏览器的最高版本为IE9。IE9也是Vue2前端框架支持的最低浏览器版本。 【准备工作】 去微…

蓝桥杯物联网竞赛_STM32L071_20_用printf将数据显示在OLED上

需求&#xff1a; 第十五届国赛确实有点变态&#xff0c;显示部分大概有6个所以需要大量将sprintf与OLED_ShowString配合使用才能显示相应格式的数据&#xff0c;所以我在想能不能简化一下这一部分直接用写好的printf语句将数据显示到显示屏上呢&#xff1f; 代码&#xff1a…

树--搜索二叉树

现有一棵结点数目为n的二叉树&#xff0c;采用二叉链表的形式存储。对于每个结点均有指向左右孩子的两个指针域&#xff0c;而结点为n的二叉树一共有n-1条有效分支路径。那么&#xff0c;则二叉链表中存在2n-(n-1)n1个空指针域。那么&#xff0c;这些空指针造成了空间浪费。 例…

【TB作品】msp430g2553单片机,秒表,LCD1602,Proteus仿真

功能 秒表 动图&#xff1a; 部分代码 这段代码是用C语言编写的&#xff0c;用于在基于德州仪器MSP430微控制器的平台上实现一个简易的电子秒表功能。 #include <msp430.h> #include "LCD.h"unsigned int second 0; unsigned int millisecond10…