UDP Socket API 的讲解,以及回显服务器客户端的实现

文章目录

    • UDP
      • DatagramSocktet API
      • DatagramPacket API
    • UDP 客户端服务器实现

UDP

先来认识一下 UDP 的 socket api,两个核心的类:DatagramSocket、DatagramPacket.

DatagramSocktet API

是一个 socket 对象。

什么是 socket?

操作系统,使用文件这样的概念,来管理一些软硬件资源。网卡,操作系统也是使用 文件 的方式来管理网卡的。表示网卡的这类文件,称为 Socket 文件。Java 中的 socket 对象,就对应 系统里的 socket 文件。

因此,想要进行网络通信,必须得先有 socket 对象。

DatagramSocket构造方法:

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

DatagramSocket() 在客户端这边使用,客户端使用哪个端口,是系统自动分配的。

一个客户端的主机,上面运行的程序很多,天知道你手动指定的端口是不是被别的程序占用了。因此,让系统自动分配一个端口是更明智的选择.

DatagramSocket(int port) 在服务器这边使用,服务器使用哪个端口,是手动指定的。

对于服务器来说,需要有一个固定的端口号,方便其他客户端找到。

DatagramSocket 方法:

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

DatagramPacket API

表示了一个 UDP 发送和接收的数据报。

代表了系统中设定的 UDP 数据报的二进制结构。

DatagramPacket 构造方法:

方法签名说明方法
DatagramPacket(byte[] buf, int length)构造一个 DatagramPacket 用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个 DatagramPacket 用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

DatagramPacket 方法:

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

UDP 客户端服务器实现

接下来手动写 UDP 客户端服务器

实现一个最简单的回显服务器 (Echo Server).

回显服务器:顾名思义,就是客户端发啥,服务器就返回啥。

我们知道,一个服务器可以供多个客户端同时使用,因此我们最先想到的是利用多线程来实现 UDP.
但事实上 UDP 服务器不需要多线程,是因为UDP是无连接的,每个数据包都是独立的,服务器只需要监听一个端口,接收数据包并处理即可。

核心思路

  1. 服务器:
    • 接收客户端发送过来是请求 ( 收到的请求是一个 DatagramPacket 类 ),并解析出请求内容 ( 转换成 String 类)。
    • 根据请求做出响应
    • 把响应返回给客户端
  2. 客户端
    • 从控制台读取用户输入的内容.
    • 构造请求对象,并发给服务器.
    • 接收服务器给出的响应,并解析出响应内容.
    • 将响应内容打印出来.
  3. 客户端服务器相互关联:通过 ip 和 端口号.
    • ip:每个服务器都有自己的 ip 地址,客户端需要通过 ip 找到服务器。(127.0.0.1 就表示自己的电脑)
    • 端口号:每个服务器有很多个端口,端口号就是用于客户端到底是访问服务器的哪个端口。

实现 UDP 会用到的方法:

getSocketAddress() :就是 getAddress() 和 getPort() 的结合体. [address:port]

InetAddress.getByName(“主机名”):如果传入的是主机名,则该方法会尝试解析该主机名,如果解析成功,则返回对应的 IP 地址;如果解析失败,则抛出 UnknownHostException 异常.

getData():获取数据报中的数据.

客户端代码:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp ;private int serverPort;public UdpEchoClient(String ip, int port) throws SocketException {serverIp = ip;serverPort = port;// 这个 new 操作,就不再指定端口了,让系统自动分配一个空闲端口socket = new DatagramSocket();}public void start () throws IOException {Scanner scanner  = new Scanner(System.in);System.out.println("客户端启动!");while (true) {// 1. 从控制台读取用户输入的内容System.out.print("->");String request = scanner.nextLine();// 2. 构造请求对象,并发给服务器DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 3. 读取服务器的响应, 并解析出响应内容.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String response = new String(responsePacket.getData(), 0, responsePacket.getLength());// 4. 显示到屏幕上System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);client.start();}
}/*客户端启动!->hellohello
*/

服务器代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;//回显服务器
//客户端发的请求是啥,服务器返回的响应就是啥
public class UdpEchoServer {private DatagramSocket socket = null;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");while(true) {// 1.读取数据,并请求DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);socket.receive(requestPacket);//  转换成字符转String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2.根据请求,计算出响应String response = process(request);// 3.把响应写回给客户端//   此时需要告知网卡,要发的内容是啥,要发给谁DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());socket.send(responsePacket);//记录日记,方便观察程序执行效果System.out.printf("[%s:%d] req: %s , resp: %s\n", responsePacket.getAddress().toString(), responsePacket.getPort(),request, response);}}//根据请求计算响应,由于是回显程序,响应内容和请求完全一样private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);    //设定服务器的端口号为 9090server.start();}
}/*服务器启动![/127.0.0.1:54015] req: hello , resp: hello
*/

问题1:我电脑上的 udp 服务器,别人可以访问吗??

答:不可以,因为我当前的电脑上没有 “外网IP”。解决办法就是买一个有外网的云服务器 😆

问题2:socket 对象用完后需要关闭吗??

答:需要,我们要知道为什么要关闭 socket 对象。最主要的就是释放系统中的 socket 文件,从而释放文件描述符

但是上述代码中我们为什么没去关闭 socket 对象呢?

因为对于咱们这个服务器来说,DatagramSocket 不关闭,问题不大。整个程序中只有一个 socket 对象,不是频繁创建的,生命周期是跟随整个进程的。但是如果是有多个 socket 对象, 且 socket 对象生命周期更短需要频繁创建释放。一定要记得去 close 。

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

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

相关文章

nginx部署多个vue或react项目

下载nginx(tar.gz) nginx: download(官方地址) 部署nginx # 进入nginx压缩包所在目录 cd /usr/nginx# 解压 tar -zxvf nginx-1.25.3.tar.gz# 安装nginx的相关依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel# 生成Makefile可编译文件 cd /usr/ng…

【JavaSE】集合(学习笔记)

一、数据结构 1、栈 压栈 / 弹栈栈顶元素、栈底元素先进后出 2、队列 入队列 / 出队列前端、后端先进先出 3、数组 查询效率高,增删效率低 4、链表 查询效率低(必须从头找),增删效率高 5、哈希表 比较方法哈希值equals结构:数组 链…

golang面试题:字符串转成byte数组,会发生内存拷贝吗?

问题 字符串转成byte数组,会发生内存拷贝吗? 怎么答 字符串转成切片,会产生拷贝。严格来说,只要是发生类型强转都会发生内存拷贝。那么问题来了。 频繁的内存拷贝操作听起来对性能不大友好。有没有什么办法可以在字符串转成切片的…

Electron+Ts+Vue+Vite桌面应用系列:TypeScript常用时间处理工具

文章目录 1️⃣ 时间处理工具1.1 格式化时间1.2 把时间戳改成日期格式1.3 Day.js 工具类使用1.4 date-fns 工具类使用 优质资源分享 作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/134712978 ElectronTsVueVite桌面应用…

华为全屋智能5.0,无为而“智”

在赖特西塔里埃森混凝土墙的中心壁龛里,一块铜牌上刻着一些英文,意思是“建筑的意义不是屋顶和墙,而是人们生活于其中的空间”。 这句话,取自老子《道德经》中的“凿户牖以为室,当其无,有室之用”。 《理想…

数据库管理-第119期 记一次迁移和性能优化(202301130)

数据库管理-第119期 记一次迁移和性能优化(202301130) 1 迁移 之前因为DV组件没有迁移成功的那个PDB,后来想着在目标端安装DV组件迁移,结果目标端装不上,而且开了SR也没看出个所以然来。只能换一个方向,尝…

VIR-SLAM代码分析3——VIR_VINS详解之estimator.cpp/.h

前言 续接上一篇,本本篇接着介绍VIR-SLAM中estimator.cpp/.h文件的函数,尤其是和UWB相关的相比于VINS改动过的函数,仍然以具体功能情况代码注释的形式进行介绍。 重点函数介绍 优化函数,代码是先优化,后边缘化。 …

docker 安装nginx

docker 安装nginx 1.打开终端或命令提示符&#xff0c;并执行以下命令来从Docker Hub下载Nginx镜像&#xff1a; docker pull nginx这将从Docker Hub下载最新的Nginx镜像到本地。 扩展&#xff1a;如果你想下载指定版本可以使用以下命令 docker pull nginx:<version>…

mysql 日志分析

程序启动标志 可以直接全局搜索&#xff0c;查看启动了几次 可以看到总共11次&#xff0c;当前是第2次 如何判断mysql是正常关闭&#xff0c;手动启动的 下图中启动之前出现 Shutdown complete打印说明启动之前是正常关闭的

TS类和接口

TS像其他语言一样&#xff0c;也有类及接口的概念。扩展类时使用extends关键字&#xff0c;使用implements关键字指明该类满足某个接口。还可以有抽象类&#xff0c;一共有三种可见效&#xff1a;private、protected及public&#xff08;默认&#xff09;。 1 基础 子类可以扩…

Serilog .net下的新兴的日志框架

Serilog .net下的新兴的日志框架 1.Serilog简介 Serilog 是针对 .NET 应用程序的流行日志记录框架。它以其灵活性、易用性和可扩展性而闻名。借助 Serilog&#xff0c;开发人员可以轻松记录应用程序中的事件、错误和消息。它支持结构化日志记录&#xff0c;能够以结构化格式存…

贪心 55. 跳跃游戏 45.跳跃游戏 II

55. 跳跃游戏 题目&#xff1a; 给定非负数组&#xff0c;初始位置在数组第一格&#xff0c;数组值是可以选择的最大跳跃步数&#xff0c;判断能不能达到数组末尾。 示例 1: * 输入: [2,3,1,1,4] * 输出: true * 解释: 我们可以先跳 1 步&#xff0c;从位置 0 到达 位置 1,…

AWS EC2 如何 使用 SSM会话管理器登陆

首先只有特定版本的OS会默认附带SSM Agent。 预安装了 SSM Agent 的 Amazon Machine Images&#xff08;AMIs&#xff09; - AWS Systems Manager 其次EC的instance role必须有一个叫“AmazonSSMManagedInstanceCore”的策略 如何给IAM User赋权&#xff0c;让他们可以使用SSM…

appium :输入框控件为android.view.View 时输入内容(如:验证码、密码输入框)

问题背景 输入密码的组件信息为&#xff1a;<android.view.View resource-id“com.qq.ac.android:id/pwd_input”> 由于输入框控件是android.view.View&#xff0c;不是android.widget.EditText&#xff0c;所以只能点击&#xff0c;而启动appium后&#xff0c;会将输入…

Vue语音播报,不用安装任何包和插件,直接调用。

Vue语音播报功能可以通过使用浏览器提供的Web Speech API来实现。这个API允许你的应用程序通过浏览器朗读文本&#xff0c;不用安装任何包和插件&#xff0c;直接调用。以下是一个简单的介绍&#xff0c;演示如何在Vue中使用语音提示功能&#xff1a; 一、JS版本 <template…

XTU OJ 1339 Interprime 学习笔记

链接 传送门 代码 #include<bits/stdc.h> using namespace std;const int N1e610; //78498 我计算了一下&#xff0c;6个0的范围内有这么多个素数&#xff0c;所以开这么大的数组存素数 //计算的代码是一个循环 int prime[80000]; int a[N],s[N];//s数组是前缀和数组b…

高等数学上岸宝典笔记

①不单调的函数也可能有反函数 ②注意反函数与函数转换时的定义域与值域 ③收敛数列不一定有最值 收敛数列必有上界和下界&#xff0c;但不一定有最值&#xff0c;比如{An}1/n&#xff0c;下界为0&#xff0c;但永远取不到0 ④数列与其子数列的关系 例题&#xff1a; ⑤带根号…

Linux常用命令——cd命令

文章目录 1. 简介2. 命令参数3. 常见用法与实例3.1 基本用法3.2 使用绝对路径或相对路径3.3 使用特殊字符3.4 使用参数 4. 总结 1. 简介 cd命令是Linux系统中最基础且频繁使用的命令之一&#xff0c;用于改变当前工作目录。它是“change directory”的缩写&#xff0c;对于任何…

什么是WebP

WebP 是一种现代的图像格式&#xff0c;由 Google 开发&#xff0c;用于在 Web 上显示图像。它使用了有损压缩算法&#xff0c;可以显著减小图像的文件大小&#xff0c;同时保持较高的图像质量。 WebP 格式的主要特点包括&#xff1a; 更小的文件大小&#xff1a;相比于 JPEG …

网狐类源码游戏配置数据库数据(一键配置网狐数据库)

网狐类源码游戏配置数据库数据&#xff08;一键配置网狐数据库&#xff09; 一般拿到网狐的源码或组件&#xff0c;需要先附加或配置数据库&#xff0c;以下为全部需要更改数据的地方&#xff0c;这里以荣耀系列版本数据库为例&#xff1a; 1. 数据库设置 [RYPlatformDB].…