网络编程-UDP套接字

news2025/3/1 9:59:01

文章目录

  • 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.coloradmin.cn/o/2278519.html

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

相关文章

3.数据库系统

3.1数据库的基本概念 3.1.1:数据库体系结构 3.1.1.1集中式数据库系统 数据是集中的 数据管理是集中的 数据库系统的素有功能(从形式的用户接口到DBMS核心)都集中在DBMS所在的计算机 3.1.1.2C/S结构 客户端负责数据表示服务服务器主要负责数据库服务 数据库系统分为前端和后端…

探索 Transformer²:大语言模型自适应的新突破

目录 一、来源&#xff1a; 论文链接&#xff1a;https://arxiv.org/pdf/2501.06252 代码链接&#xff1a;SakanaAI/self-adaptive-llms 论文发布时间&#xff1a;2025年1月14日 二、论文概述&#xff1a; 图1 Transformer 概述 图2 训练及推理方法概述 图3 基于提示的…

【北京迅为】iTOP-4412全能版使用手册-第八十七章 安装Android Studio

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

LDD3学习8--linux的设备模型(TODO)

在LDD3的十四章&#xff0c;是Linux设备模型&#xff0c;其中也有说到这个部分。 我的理解是自动在应用层也就是用户空间实现设备管理&#xff0c;处理内核的设备事件。 事件来自sysfs和/sbin/hotplug。在驱动中&#xff0c;只要是使用了新版的函数&#xff0c;相应的事件就会…

Jira中bug的流转流程

Jira中bug的状态 1. 处理Bug的流程2. bug状态流转详述bug的状态通常包括 1. 处理Bug的流程 2. bug状态流转详述 bug的状态通常包括 未解决 1. 测试人员创建一个bug&#xff0c;填写bug的详细信息&#xff0c;如概要、bug级别、复现步骤、现状、预期结果等 2. 定位bug&#x…

解决关于Xcode16提交审核报错

# 问题描述 The following issues occurred while distributing your application. Asset validation failed Invalid Executable. The executable xxx.app/Frameworks/HappyDNS.framework/HappyDNS contains bitcode.(lD:ef5dd249-731f-4731-8173-8e4a12519352) Asset valida…

windows下安装并使用node.js

一、下载Node.js 选择对应你系统的Node.js版本下载 Node.js官网下载地址 Node.js中文网下载地址??? 这里我选择的是Windows64位系统的Node.js20.18.0&#xff08;LTS长期支持版本&#xff09;版本的.msi安装包程序 官网下载&#xff1a; 中文网下载&#xff1a; 二、安…

基于SpringBoot+Vue旅游管理系统的设计和实现(源码+文档+部署讲解)

个人名片 &#x1f525; 源码获取 | 毕设定制| 商务合作&#xff1a;《个人名片》 ⛺️心若有所向往,何惧道阻且长 文章目录 个人名片环境需要技术栈功能介绍功能说明 环境需要 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&…

python之二维几何学习笔记

一、概要 资料来源《机械工程师Python编程&#xff1a;入门、实战与进阶》安琪儿索拉奥尔巴塞塔 2024年6月 点和向量&#xff1a;向量的缩放、范数、点乘、叉乘、旋转、平行、垂直、夹角直线和线段&#xff1a;线段中点、离线段最近的点、线段的交点、直线交点、线段的垂直平…

RabbitMQ---消息确认和持久化

&#xff08;一&#xff09;消息确认 1.概念 生产者发送消息后&#xff0c;到达消费端会有以下情况&#xff1a; 1.消息处理成功 2.消息处理异常 如果RabbitMQ把消息发送给消费者后就把消息删除&#xff0c;那么就可能会导致&#xff0c;消息处理异常想要再获取这条消息的时…

map和set c++

关联式容器也是⽤来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;关联式容器逻辑结构通常是⾮线性结构&#xff0c;两个位置有紧密的关联关系&#xff0c;交换⼀下&#xff0c;他的存储结构就被破坏了。顺序容器中的元素是按关键字来保存和访问的。关联式容器有map/…

turtle教学课程课堂学习考试在线网站

完整源码项目包获取→点击文章末尾名片&#xff01;

Digital Document System (DDS)

Digital Document System (DDS&#xff09; 数字档案平台 信息注入

Springer Nature——Applied Intelligence 投稿指南

投稿系统&#xff1a;Editorial Manager (Manuscript and Peer Review) : 使用Editorial Manager 投稿系统的期刊列表&#xff1a;期刊列表 期刊主页&#xff1a;Spring Nature 主页 投稿主页&#xff1a;Spring Nature Submit SystemSubmission Guidelines: Official Submissi…

如何在前端给视频进行去除绿幕并替换背景?-----Vue3!!

最近在做这个这项目奇店桶装水小程序V1.3.9安装包骑手端V2.0.1小程序前端 最近&#xff0c;我在进行前端开发时&#xff0c;遇到了一个难题“如何给前端的视频进行去除绿幕并替换背景”。这是一个“数字人项目”所需&#xff0c;我一直在冥思苦想。终于有了一个解决方法…

使用python+pytest+requests完成自动化接口测试(包括html报告的生成和日志记录以及层级的封装(包括调用Json文件))

一、API的选择 我们进行接口测试需要API文档和系统&#xff0c;我们选择JSONPlaceholder免费API&#xff0c;因为它是一个非常适合进行接口测试、API 测试和学习的工具。它免费、易于使用、无需认证&#xff0c;能够快速帮助开发者模拟常见的接口操作&#xff08;增、删、改、…

UE4原生的增量Cook原理

设置Cook的步骤后&#xff0c;断点进入到如下堆栈&#xff1a; UCookOnTheFlyServer::StartCookByTheBook(const UCookOnTheFlyServer::FCookByTheBookStartupOptions &) CookOnTheFlyServer.cpp:7723 UCookCommandlet::CookByTheBook(const TArray<…> &, TArr…

C#表达式和运算符

本文我们将学习C#的两个重要知识点&#xff1a;表达式和运算符。本章内容会理论性稍微强些&#xff0c;我们会尽量多举例进行说明。建议大家边阅读边思考&#xff0c;如果还能边实践就更好了。 1. 表达式 说到表达式&#xff0c;大家可能感觉有些陌生&#xff0c;我们先来举个…

蓝桥杯 Python 组知识点容斥原理

容斥原理 这张图初中或者高中数学课应该画过 也就是通过这个简单的例子引出容斥原理的公式 这张图的面积&#xff1a;s1 s3 s7 - 2 * s2 - 2 * s4 - 2 * s6 3 * s5 通过此引导出容斥原理公式 那么下面来一起看看题目 题目描述 给定 n,m 请求出所有 n 位十进制整数中有多…

PDF文件提取开源工具调研总结

概述 PDF是一种日常工作中广泛使用的跨平台文档格式&#xff0c;常常包含丰富的内容&#xff1a;包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用&#xff0c;尤其是RAG项目中&#xff0c;通过将非结构化数据转化为结构化和可访问的信息&#xff0…