网络编程-UDP套接字

文章目录

  • UDP/TCP协议简介
    • 两种协议的联系与区别
    • Socket是什么
  • UDP的SocketAPI
    • DatagramSocket
    • DatagramPacket
  • 使用UDP模拟通信
    • 服务器端
    • 客户端
    • 测试
  • 完整测试代码

UDP/TCP协议简介

两种协议的联系与区别

TCP和UDP其实是传输层的两个协议的内容, 差别非常大, 对于我们的Java来说, JVM对操作系统提供的关于网络的 API 进行了封装, 提供了两套的API


下面是网络连接中的一些特点

  • 有/无连接: 抽象的概念, 虚拟的, 逻辑上的连接, 而不是物理的连接, 其实就是看, 在网络通信的过程中, 是否保存了对端的一些信息, 比如说IP, 端口号之类的
  • 可靠传输/不可靠传输: 网络传输的过程中, 传输的信息是十分容易丢失的, 不可能100%的到达, 这里说的可靠传输还是不可靠传输是指的是, 尽可能的到达, 可靠传输, 发送消息之后, 会尽可能的提高传输的成功率, 如果出现了丢包的问题, 对面也能感知到, 但是对于不可靠传输, 发送消息之后就不管了, 只是简单的发送了数据
  • 面向字节流/数据报: 指的是传输的方式, 有的协议使用字节流进行内容的传输, 容易粘包, 支持任意长度, 有的协议使用数据报进行内容的传输, 不存在粘包, 但是有长度限制
  • 全双工/半双工: 一个通信的链路, 支持双向的通信, 能读, 也能写, 但是有的通信的协议只支持单向的通信, 要么读, 要么写

下面是 UDP 协议和 TCP 协议的特点的声明

UDPTCP
无连接有连接
不可靠传输可靠传输
面向数据报面向字节流
全双工全双工

Socket是什么

可以理解为是一个网卡的代言人, 在计算机中来说, 文件其实是一种广义的概念, 网卡我们也抽象为一种Socket文件, 所以操作网卡的流程中, 是与文件的操作是差不多的, 对网卡的操作, 其实是对Socket这种文件类型的操作, 也会占用文件操作符表(文件操作中的一种资源), 所以也要及时关闭

  • 打开 -> 读写 -> 关闭

UDP的SocketAPI

DatagramSocket

上面我们说过, 每一种套接字都有自己的一套 API, 而UDP的操作网卡的 API 就是 DatagramSocket


常见的构造方法

在这里插入图片描述
上图的两个构造方法是我们常用的两个方法

  • 第一个是不带端口号的版本, 所以定义之后, 会给当前的程序随机分配一个端口号(一般用于客户端)
  • 第二个参数是给一个指定的端口号(一般用于服务器端)
  • 如果一台服务器上有多个UDP程序使用同一个端口号, 那就会出现问题, 端口号冲突, 但是如果同一台计算机上不同协议的程序使用同一个端口号不会冲突, 比如一个UDP程序使用端口号9090, 另一个TCP程序也使用9090, 这种情况就不会冲突

常用的方法

在这里插入图片描述
send方法是发送构造好的DatagramPacket对象(其实就是数据报), receive是一种输出型函数的机制, 通常是传入一个空的DatagramPacket对象, 然后把接收到的内容填入到这个对象内部, 如果没有客户端发送数据, 该方法就会陷入阻塞等待阶段


在这里插入图片描述

close方法, 关闭该套接字

观察这个类的继承结构

在这里插入图片描述
该类继承了AutoCloseable接口, 所以也支持try-with-resource机制

DatagramPacket

该类本质上是一个数据报


常见的构造方法

在这里插入图片描述
我们在之前就说过, UDP是一种无连接的协议, 也就是在网卡层面是不保存对端的信息的, 那我们要如果知道数据发送给哪一台机器呢 ? 实质上就是通过DatagramPacket来实现的, 这个数据报通常保存了对端的信息, 而传输的内容是通过字符数组来保存的, InetAddress其实是IP地址的信息, port是对端的端口号, SocketAddress可以理解为是InetAddress和port的结合, 里面既有IP信息还有端口号信息


常见的方法

在这里插入图片描述
注意:
对于一个数据报对象来说, 里面存储的地址的信息, 不仅包含接收方的地址信息, 还保存着发送方的地址信息, 所以想要在服务器端做出响应的时候, 对于发送的地址, 是从接收到的DatagramPacket对象中获取到的, 因为里面也保存了客户端的地址信息

  • getAddress获取的是IP地址, 既可以是发送端的, 也可以是接收端的
  • getPort获取的是端口号, 同上
  • getSocketAddress获取的是完整的地址信息, 同上
  • getLength获取的是接收到的数据的真实长度(以字节计)

比如下面的代码

在这里插入图片描述

这种情况下返回的就是发送端的地址信息

在比如服务器给客户端返回结果的时候, 使用接收到的DatagramPacket对象的getAddress, getPort, getSocketAddress方法, 此时得到的就是发送方(也就是客户端)的地址信息

所以, 获取到的是哪一端的地址信息要看实际的情况

使用UDP模拟通信

关于计算机通信的机制, 我们之前的版块涉及到一点, 大致流程如下

在这里插入图片描述


服务器端

写一个执行翻译的服务器

创建网卡还有构造方法

	// 创建一个网卡对象private DatagramSocket serverSocket = null;// 构造方法(服务器端固定端口号)public UdpServer(int port) throws SocketException {serverSocket = new DatagramSocket(port);}

start方法, 启动服务器, 不断接收用户的请求, 处理并响应
这里我们只是简单模拟一下, 真实的业务场景中, 这里的逻辑是相当相当复杂的, 所以处理时间可能会很长, 所以如果此时有别的客户端想请求的话, 那就有可能得不到及时的响应, 所以我们此时可以采用多线程的技术, 使用线程池来优化, 具体代码我们最后的完整代码会给出

// start方法开启服务器public void start() throws IOException {// 记录日志, UDP 服务器上线System.out.println("UDP服务器上线");// 使用while循环不断接收客户端的请求while(true){// 1. 读取请求(使用一个空数据报来接收客户端数据, 输出型函数)DatagramPacket request = new DatagramPacket(new byte[4096], 0, 4096);serverSocket.receive(request);// 2. 解析请求并处理String req = new String(request.getData(), 0, request.getLength());String resp = process(req);// 3. 返回响应(发送处理的结果)DatagramPacket responce = new DatagramPacket(resp.getBytes(), 0, resp.getBytes().length,request.getSocketAddress());serverSocket.send(responce);// 4. 记录日志信息System.out.printf("[%s, %d] req:%s  resp:%s\n", request.getAddress().toString(), request.getPort(), req, resp);}}

处理的核心逻辑

// 对请求处理的逻辑private static Map<String, String> chineseToEnglish = new HashMap<>();static {chineseToEnglish.put("小猫", "cat");chineseToEnglish.put("小狗", "dog");chineseToEnglish.put("小鹿", "fawn");chineseToEnglish.put("小鸟", "bird");}private String process(String req){return chineseToEnglish.getOrDefault(req, "未收录该词条");}

客户端

关于客户端其实和服务器端差不多, 也是发送请求和接收响应的逻辑

创建网卡, 构造方法, 还有创建变量来保存对端的地址信息(构造数据报使用)

// 创建网卡private DatagramSocket clientSocket = null;// 创建变量保存对端信息private InetAddress serverInet = null;private int serverPort = 0;// 构造方法(客户端一般是随机的端口号)public UdpClient(String iNetAddr, int serverPort) throws UnknownHostException, SocketException {this.serverPort = serverPort;this.serverInet = InetAddress.getByName(iNetAddr);clientSocket = new DatagramSocket();}

start方法, 请求并响应

while(sc.hasNext()){// 1. 输入并发送请求String req = sc.next();DatagramPacket request = new DatagramPacket(req.getBytes(), 0, req.getBytes().length, serverInet, serverPort);clientSocket.send(request);// 2. 等待请求响应DatagramPacket responce = new DatagramPacket(new byte[4096], 0, 4096);clientSocket.receive(responce);String resp = new String(responce.getData(), 0, responce.getLength());// 3. 输出响应结果System.out.println(resp);}

测试

下面是上面的代码的运行测试截图
在这里插入图片描述

在这里插入图片描述

完整测试代码

客户端

package net_demo1.net_demo03;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpClient {// 创建网卡private DatagramSocket clientSocket = null;// 创建变量保存对端信息private InetAddress serverInet = null;private int serverPort = 0;// 构造方法(客户端一般是随机的端口号)public UdpClient(String iNetAddr, int serverPort) throws UnknownHostException, SocketException {this.serverPort = serverPort;this.serverInet = InetAddress.getByName(iNetAddr);clientSocket = new DatagramSocket();}// start方法, 启动客户端public void start() throws IOException {// 创建一个Scanner对象接收用户输入Scanner sc = new Scanner(System.in);// 使用while循环来请求并接收响应while(sc.hasNext()){// 1. 输入并发送请求String req = sc.next();DatagramPacket request = new DatagramPacket(req.getBytes(), 0, req.getBytes().length, serverInet, serverPort);clientSocket.send(request);// 2. 等待请求响应DatagramPacket responce = new DatagramPacket(new byte[4096], 0, 4096);clientSocket.receive(responce);String resp = new String(responce.getData(), 0, responce.getLength());// 3. 输出响应结果System.out.println(resp);}}public static void main(String[] args) throws IOException {UdpClient udpClient = new UdpClient("127.0.0.1", 9090);udpClient.start();}
}

服务器端

package net_demo1.net_demo03;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class UdpServer {// 创建一个网卡对象private DatagramSocket serverSocket = null;// 构造方法(服务器端固定端口号)public UdpServer(int port) throws SocketException {serverSocket = new DatagramSocket(port);}// start方法开启服务器public void start() throws IOException {// 记录日志, UDP 服务器上线System.out.println("UDP服务器上线");// 创建一个线程池ExecutorService executorService = Executors.newCachedThreadPool();// 使用while循环不断接收客户端的请求while (true) {// 1. 读取请求(使用一个空数据报来接收客户端数据, 输出型函数)DatagramPacket request = new DatagramPacket(new byte[4096], 0, 4096);serverSocket.receive(request);// 这里的改进, 由于我们处理的时间可能会很长, 如果此时有别的客户端也请求了, 那就可能造成数据丢失// 所以我们使用线程池的技术, 通过多线程来执行任务executorService.execute(() -> {String req = new String(request.getData(), 0, request.getLength());String resp = process(req);// 3. 返回响应(发送处理的结果)DatagramPacket responce = new DatagramPacket(resp.getBytes(), 0, resp.getBytes().length,request.getSocketAddress());try {serverSocket.send(responce);} catch (IOException e) {e.printStackTrace();}// 4. 记录日志信息System.out.printf("[%s, %d] req:%s  resp:%s\n",request.getAddress().toString(), request.getPort(), req, resp);});}}// 对请求处理的逻辑private static Map<String, String> chineseToEnglish = new HashMap<>();static {chineseToEnglish.put("小猫", "cat");chineseToEnglish.put("小狗", "dog");chineseToEnglish.put("小鹿", "fawn");chineseToEnglish.put("小鸟", "bird");}private String process(String req) {return chineseToEnglish.getOrDefault(req, "未收录该词条");}public static void main(String[] args) throws IOException {UdpServer udpServer = new UdpServer(9090);udpServer.start();}
}

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

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

相关文章

Unity补充 -- 协程相关

1.协程。 协程并不是线程。线程是主线程之外的另一条 代码按照逻辑执行通道。协程则是在代码在按照逻辑执行的同时&#xff0c;是否需要执行额外的语句块。 2.协程的作用。 在update执行的时候&#xff0c;是按照帧来进行刷新的&#xff0c;也是按照帧执行代码的。但是又不想…

IoTDB 常见问题 QA 第四期

关于 IoTDB 的 Q & A IoTDB Q&A 第四期来啦&#xff01;我们将定期汇总我们将定期汇总社区讨论频繁的问题&#xff0c;并展开进行详细回答&#xff0c;通过积累常见问题“小百科”&#xff0c;方便大家使用 IoTDB。 Q1&#xff1a;Java 中如何使用 SSL 连接 IoTDB 问题…

Json转换类型报错问题:java.lang.Integer cannot be cast to java.math.BigDecimal

Json转换类型报错问题&#xff1a;java.lang.Integer cannot be cast to java.math.BigDecimal 小坑规避指南 小坑规避指南 项目中遇到json格式转换成Map&#xff0c;已经定义了Map的key和value的类型&#xff0c;但是在遍历Map取值的时候出现了类型转换的报错问题&#xff08…

数据结构——队列和栈(介绍、类型、Java手搓实现循环队列)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

python http server运行Angular 单页面路由时重定向,解决404问题

问题 当Angular在本地ng server运行时候&#xff0c;可以顺利访问各级路由。 但是运行ng build后&#xff0c;在dist 路径下的打包好的额index.html 必须要在服务器下运行才能加载。 在服务器下我们第一次访问路由页面时是没有问题的&#xff0c;但是尝试刷新页面或手动输入路…

SQL表间关联查询详解

简介 本文主要讲解SQL语句中常用的表间关联查询方式&#xff0c;包括&#xff1a;左连接&#xff08;left join&#xff09;、右连接&#xff08;right join&#xff09;、全连接&#xff08;full join&#xff09;、内连接&#xff08;inner join&#xff09;、交叉连接&…

Android Jni(一) 快速使用

文章目录 Android Jni&#xff08;一&#xff09; 快速使用1、 环境配置下载 NDK2、右键 add c to module3、创建一个 native 方法&#xff0c;并更具提示&#xff0c;自动创建对应的 JNI 实现4、实现对应 Jni 方法5、static loadLibrary6、调用执行 遇到的问题1、[CXX1300] CM…

【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局

目录 1 -> 自适应布局 1.1 -> 线性布局 1.1.1 -> 线性布局的排列 1.1.2 -> 自适应拉伸 1.1.3 -> 自适应缩放 1.1.4 -> 定位能力 1.1.5 -> 自适应延伸 1.2 -> 层叠布局 1.2.1 -> 对齐方式 1.2.2 -> Z序控制 1.3 -> 弹性布局 1.3.1…

物联网网关Web服务器--Boa服务器移植与测试

1、Boa服务器介绍 BOA 服务器是一个小巧高效的web服务器&#xff0c;是一个运行于unix或linux下的&#xff0c;支持CGI的、适合于嵌入式系统的单任务的http服务器&#xff0c;源代码开放、性能高。 Boa 嵌入式 web 服务器的官方网站是http://www.boa.org/。 特点 轻量级&#x…

tomcat状态一直是Exited (1)

docker run -di -p 80:8080 --nametomcat001 你的仓库地址/tomcat:9执行此命令后tomcat一直是Exited(1)状态 解决办法&#xff1a; 用以下命令创建运行 docker run -it --name tomcat001 -p 80:8080 -d 你的仓库地址/tomcat:9 /bin/bash最终结果 tomcat成功启动

三天急速通关Java基础知识:Day1 基本语法

三天急速通关JAVA基础知识&#xff1a;Day1 基本语法 0 文章说明1 关键字 Keywords2 注释 Comments2.1 单行注释2.2 多行注释2.3 文档注释 3 数据类型 Data Types3.1 基本数据类型3.2 引用数据类型 4 变量与常量 Variables and Constant5 运算符 Operators6 字符串 String7 输入…

表单中在不设置required的情况下在label前加*必填标识

参考:https://blog.csdn.net/qq_55798464/article/details/136233718 需求&#xff1a;在发票类型前面添加*必填标识 我最开始直接给发票类型这个表单类型添加了验证规则required:true&#xff0c;问题来了&#xff0c;这个发票类型它是有默认值的&#xff0c;所以我点击保存…

2025寒假备战蓝桥杯01---朴素二分查找的学习

文章目录 1.暴力方法的引入2.暴力解法的思考 与改进3.朴素二分查找的引入4.朴素二分查找的流程5.朴素二分查找的细节6.朴素二分查找的题目 1.暴力方法的引入 对于下面的这个有序的数据元素的组合&#xff0c;我们的暴力解法就是挨个进行遍历操作&#xff0c;一直找到和我们的这…

ROS机器人学习和研究的势-道-术-转型和变革的长期主义习惯

知易行难。说说容易做到难。 例如&#xff0c;不受成败评价影响&#xff0c;坚持做一件事情10年以上&#xff0c;专注事情本身。 机器人专业不合格且失败讲师如何让内心保持充盈的正能量&#xff08;节选&#xff09;-CSDN博客 时间积累 注册20年。 创作历程10年。 创作10年…

渗透测试之XEE[外部实体注入]漏洞 原理 攻击手法 xml语言结构 防御手法

目录 原理 XML语言解释 什么是xml语言&#xff1a; 以PHP举例xml外部实体注入 XML语言结构 面试题目 如何寻找xxe漏洞 XEE漏洞修复域防御 提高版本 代码修复 php java python 手动黑名单过滤(不推荐) 一篇文章带你深入理解漏洞之 XXE 漏洞 - 先知社区 原理 XXE&…

BUUCTF_Web([GYCTF2020]Ezsqli)

1.输入1 &#xff0c;正常回显。 2.输入1 &#xff0c;报错false&#xff0c;为字符型注入&#xff0c;单引号闭合。 原因&#xff1a; https://mp.csdn.net/mp_blog/creation/editor/145170456 3.尝试查询字段&#xff0c;回显位置&#xff0c;数据库&#xff0c;都是这个。…

react使用react-redux状态管理

1、安装 npm install react-redux2、创建store.js import { createStore } from redux;// 定义初始状态 const initialState {counter: 888 };// 定义 reducer 函数&#xff0c;根据 action 类型更新状态 function reducer(state initialState, action) {switch (action.ty…

JAVA使用自定义注解,在项目中实现EXCEL文件的导出

首先定义一个注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.FIELD) public interface Excel {/*** 导出时在excel中排序*/int sort() default Integer.MAX_VALUE;/*** 导出到Excel中的名字.*/String name() default "";/*** 首行字段的批注*/String …

【2024年华为OD机试】 (B卷,100分)- 金字塔,BOSS的收入(Java JS PythonC/C++)

一、问题描述 微商模式收入计算 题目描述 微商模式中&#xff0c;下级每赚 100 元就要上交 15 元。给定每个级别的收入&#xff0c;求出金字塔尖上的人的收入。 输入描述 第一行输入 N&#xff0c;表示有 N 个代理商上下级关系。接下来输入 N 行&#xff0c;每行三个数&am…

光伏储能交直流微电网Matlab/Simulink仿真模型

博士毕业后项目和课题的交接工作也都基本上结束了&#xff0c;之前从20年我博一开始创作的博客&#xff0c;我也将从25年伊始重新进行更新&#xff0c;在保留原有内容的基础上&#xff0c;在对现如今的研究热点进行补充&#xff0c;希望能为各位校友提供一定的研究思路。首先是…