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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【华为路由/交换机的ssh远程设置】

华为路由/交换机的ssh远程设置 R1&#xff08;client&#xff09;&#xff1a;10.1.1.1 R2&#xff08;server&#xff09;&#xff1a;10.1.1.2 R2服务端配置&#xff1a; 生成本机密钥 查看生成的密钥 设置AAA授权验证方式&#xff0c;并设置支持SSH协议 创建本地用户&…

计算机毕业设计PySpark+Hadoop+Hive机票预测 飞机票航班数据分析可视化大屏 航班预测系统 机票爬虫 飞机票推荐系统 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

CSS笔记基础篇01——选择器、文字控制属性、背景属性、显示模式、盒子模型

黑马程序员视频地址&#xff1a; 前端Web开发HTML5CSS3移动web视频教程https://www.bilibili.com/video/BV1kM4y127Li?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodeshttps://www.bilibili.com/video/BV1kM4y127Li?vd_source0a2d3666…

C++学习第五天

创作过程中难免有不足&#xff0c;若您发现本文内容有误&#xff0c;恳请不吝赐教。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、构造函数 问题1 关于编译器生成的默认成员函数&#xff0c;很多童鞋会有疑惑&#xff1a;不实现构造函数的情况下…

2024嵌入式系统的未来发展与技术洞察分享

时间如白驹过隙&#xff0c;不知不觉又是一年&#xff0c;这一年收获满满。接下来&#xff0c;将本年度对技术的感悟和洞察分析如下&#xff0c;希望对大家有所帮助。 在过去几十年里&#xff0c;嵌入式系统技术迅速发展&#xff0c;成为现代电子设备和智能硬件的核心组成部分。…

01设计模式(D3_设计模式类型 - D3_行为型模式)

目录 一、模版方法模式 1. 基本介绍 2. 应用案例一&#xff1a;豆浆制作问题 需求 代码实现 模板方法模式的钩子方法 3. View的draw&#xff08;Android&#xff09; Android中View的draw方法就是使用了模板方法模式 模板方法模式在 Spring 框架应用的源码分析 知识小…

Linux 中如何使用 inotify-tools 监控目录变化 ?

当 Linux 系统目录中有新文件创建时执行命令&#xff0c;可以通过组合使用工具和脚本实现监控。一种常见的方法是使用 inotify-tools&#xff0c;这是一个允许您监视文件系统事件的实用程序&#xff0c;与 shell 脚本一起使用。 Step 1: 安装 inotify-tools 首先&#xff0c;…

vid2vid-zero:使用Stable Diffusion进行零样本视频编辑

Paper: Wang W, Jiang Y, Xie K, et al. Zero-shot video editing using off-the-shelf image diffusion models[J]. arXiv preprint arXiv:2303.17599, 2023. Introduction: Unreleased Code: https://github.com/baaivision/vid2vid-zero 目录 一. 预备知识1. diffusion 引导…

AI 大爆发时代,音视频未来路在何方?

AI 大模型突然大火了 回顾2024年&#xff0c;计算机领域最大的变革应该就是大模型进一步火爆了。回顾下大模型的发展历程&#xff1a; 萌芽期&#xff1a;&#xff08;1950-2005&#xff09; 1956年&#xff1a;计算机专家约翰麦卡锡首次提出“人工智能”概念&#xff0c;标志…

蓝桥杯训练—完美的代价

文章目录 一、题目二、示例三、解析四、代码 一、题目 回文串&#xff0c;是一种特殊的字符串&#xff0c;它从左往右读和从右往左读是一样的。现在给你一个串&#xff0c;它不一定是回文的&#xff0c;请你计算最少的交换次数使得该串变成一个完美的回文串。 交换的定义是&am…

雷电9最新版安装Magisk+LSPosd(新手速通)

大家好啊&#xff01;我是NiJiMingCheng 我的博客&#xff1a;NiJiMingCheng 在安卓系统的定制与拓展过程中&#xff0c;获取 ROOT 权限以及安装各类框架是进阶玩家常用的操作&#xff0c;这可以帮助我们实现更多系统层面的个性化功能。今天&#xff0c;我将为大家详细介绍如何…

【25】Word:林涵-科普文章❗

目录 题目​ NO1.2.3 NO4.5.6 NO7.8 NO9.10 NO11.12 不连续选择&#xff1a;按住ctrl按键&#xff0c;不连续选择连续选择&#xff1a;按住shift按键&#xff0c;选择第一个&#xff0c;选择最后一个。中间部分全部被选择 题目 NO1.2.3 布局→纸张方向&#xff1a;横向…

ESP8266-01S、手机、STM32连接

1、ESP8266-01S的工作原理 1.1、AP和STA ESP8266-01S为WIFI的透传模块&#xff0c;主要模式如下图&#xff1a; 上节说到&#xff0c;我们需要用到AT固件进行局域网应用&#xff08;ESP8266连接的STM32和手机进行连接&#xff09;。 ESP8266为一个WiFi透传模块&#xff0c;和…

Vscode:问题解决办法 及 Tips 总结

Visual Studio Code&#xff08;简称VSCode&#xff09;是一个功能强大的开源代码编辑器&#xff0c;广泛用于各种编程语言和开发场景&#xff0c;本博客主要记录在使用 VSCode 进行verilog开发时遇到的问题及解决办法&#xff0c;使用过程中的技巧 文章目录 扩展安装失败调试配…

Comment(爆破+git泄漏+二次注入)

通过爆破密码的后三位&#xff0c;获得账号为&#xff1a;zhangwei666 F12查看控制台 使用bugscanteam的githack工具&#xff0c;下载泄漏的源码&#xff0c;根据控制台的提示&#xff0c;完整源码还在历史的commit中 git log –reflog 查看历史记录 查看最新的提交记录&#…

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

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

vim文本编辑器

vim命令的使用&#xff1a; [rootxxx ~]# touch aa.txt #首先创建一个文件 [rootxxx ~]# vim aa.txt #vim进入文件aa.txt进行编辑 vim是vi的升级版&#xff0c;具有以下三种基本模式&#xff1a; 输入模式(编辑模式) 点击i进入编辑模式 &#xff08;说明…

MYSQL主从同步----1、基于binlog 2、基于gtid

目录 前提&#xff08;无论是基于binlog还是基于gitd&#xff09; 一、对MySQL主从同步的了解 1、MySQL支持的复制类型 2、为什么要做主从复制 3、主从复制原理---面试会考 二、基于binlog的主从同步 1、主库上的操作 1&#xff09;配置文件 2&#xff09;备份数据库&am…

opencv笔记2

图像灰度 彩色图像转化为灰度图像的过程是图像的灰度化处理。彩色图像中的每个像素的颜色由R&#xff0c;G&#xff0c;B三个分量决定&#xff0c;而每个分量中可取值0-255&#xff0c;这样一个像素点可以有256*256*256变化。而灰度图像是R&#xff0c;G&#xff0c;B三个分量…

Hadoop•用Web UI查看Hadoop状态词频统计

听说这里是目录哦 通过Web UI查看Hadoop运行状态&#x1f407;一、关闭防火墙二、在物理计算机添加集群的IP映射三、启动集群四、进入HDFS的Web UI 词频统计&#x1f9a9;1、准备文本数据2、在HDFS创建目录3、上传文件4、查看文件是否上传成功5、运行MapReduce程序6、查看MapRe…