网络编程-UDP数据报套接字

news2024/12/26 11:24:11

专栏简介: JavaEE从入门到进阶

题目来源: leetcode,牛客,剑指offer.

创作目标: 记录学习JavaEE学习历程

希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长.

学历代表过去,能力代表现在,学习能力代表未来! 


目录

1. 网络编程基础

1.1 为什么需要网络编程?

1.2 什么是网络编程?

1.3 网络编程中的基本概念

2. Socket套接字

2.1 Java 数据报套接字模型 

2.2 Java 流套接字通信模型

 2.3 Socket 编程注意事项

3.UDP 数据报套接字编程

3.1 DatagramSocket API

3.2 DatagramPacket API

3.3 InetSocketAddress API

示例一: 回显服务器(echo server)

示例二: 查词典服务器 


1. 网络编程基础

1.1 为什么需要网络编程?

用户在浏览器中 , 打开在线视频网站 , 如腾讯视频 , 实际是通过网络获取网络上的一个视频资源.

与打开本地视频文件类似 , 只不过视频资源来源是网络. 相比本地视频来说 , 网络提供了更为丰富的网络资源.

所谓的网络资源其实就是网络中可以获取的各种数据资源.

而所有的网络资源都是通过网络编程来进行数据传输的.


1.2 什么是网络编程?

网络编程指的是 , 网络上的主机通过不同的进程(端口) , 以编程的方式实现网络通信(网络数据传输)

其实 , 网络传输只要满足不同进程就行 , 因此即使是同一主机 , 只要是不同进程就可以进行网络传输.

对于开发人员来说 , 在条件有限的情况下 , 都是在同一主机上进行网络编程.

但是 , 我们一定要明确 , 我们的目的是提供网络上不同主机 , 基于网络来传输数据资源.

  • 进程A , 编程来获取网络资源
  • 进程B , 编程来提供网络资源

1.3 网络编程中的基本概念

发送端和接收端

再一次网络数据传输时:

发送端: 数据的发送方进程 , 发送端主机即网络通信中的源主机.

接收端: 数据的接收方进程 , 接收端主机即网络通信中的目的主机.

收发端: 发送端和接收端两端 , 简称收发端.

Tips: 发送端和接收端是相对的 , 只是一次网络数据传输产生数据流向后的概念.


请求和相应

一般来说 , 获取一个网络资源 , 涉及到两次网络数据传输:

  • 第一次: 请求数据的发送
  • 第二次: 响应数据的发送

例如在快餐店点一份炸鸡:

先要发起请求: 点一份炸鸡 , 之后快餐店提供对应的响应: 提供一份炸鸡.


客户端和服务器

服务端: 在常见的网络数据传输场景下 , 把提供服务的一方进程 , 称为服务器 , 可以提供对外服务.

客户端: 获取服务的一方进程 , 称为客户端.

对于服务来说 , 一般是提供:

  • 客户端获取服务资源.

  • 客户端保存资源在服务器.


常见的客户端服务端模型

1. 客户端先发送请求到服务器.

2. 服务端根据请求数据 , 执行响应的业务处理.

3. 服务端返回响应 ,发送业务的处理结果. 

4. 客户端根据响应数据 , 展示处理结果(展示获取的资源 , 或提示保存资源的处理结果).


2. Socket套接字

概念:

Socket套接字 , 是由系统提供用于网络通信的技术 , 是基于 TCP/IP 协议的网络通信的基本操作单元. 基于 Socket 套接字的网络程序开发就是网络编程.


分类:

Socket 套接字主要针对传输层协议划分为如下三类:

流套接字:

使用传输层 TCP 协议 , 即 Transmission Control Protocol (传输控制协议) , 传输层协议.

TCP 的特点:

  • 有连接
  • 可靠传输
  • 面向字节流
  • 有接收缓冲区 , 也有发送缓冲区.(全双工)
  • 大小不限

对于字节流来说 , 可以简单的理解为 , 传输数据是基于 IO 流 , 流式数据的特征就是 IO 没有关闭的情况下 , 是无边界的数据 , 可以多次发送 , 也可以分开多次接收.


数据报套接字:

使用传输层 UDP 协议 , 即User Dategram Protocol (用户数据报协议) , 传输层协议.

UDP 的特点:

  • 无连接
  • 不可靠传输
  • 面向数据报
  • 有接收缓冲区 , 也有发送缓冲区(全双工)
  • 大小不限

对于数据报来说 , 传输数据是一块一块的 , 发送一块100个字节的数据 , 必须一次发送 , 接收也必须一次接收 100 个字节 , 而不能分 100 次 , 每次接收一个字节.


原始套接字

原始套接字用于自定义传输层协议 , 用于读写内核没有处理的 IP 协议数据.


2.1 Java 数据报套接字模型 

对应 UDP 数据报来说 , 具有无连接 , 面向数据报的特征 , 即每次都是没有建立连接 , 并且一次发送全部数据报 , 一次接收全部数据报.

java 总使用 UDP 协议通信 , 主要基于 DatagramSocket 类来创建数据报套接字 , 并使用 DatagramPacket 来发送或接收 UDP 数据报. 对应发送及接收数据报的流程如下:


 2.2 Socket 编程注意事项

  • 1. 客户端和服务端: 开发时 , 经常是基于一个主机开启两个进程作为客户端和服务端 , 但      真实的场景一般都是不同主机.
  • 2. 注意目的IP目的端口号 , 标识了一次数据传输时要发送数据的终点主机和进程.
  • 3. Socket 编程我们是使用流套接字和数据报套接字 , 基于传输层的TCP或UDP协议 , 但      应用层协议 , 也需要考虑 , 后续会介绍如何设计应用层协议.
  • 4. 如果一个进程 A 绑定一个端口 , 再启动一个进程 B 绑定该端口 , 就会报错 , 这种情况     也叫做端口被占用.

3.UDP 数据报套接字编程

3.1 DatagramSocket API

DatagramSocket 是 UDP 套接字 , 用于发送和接收数据报.

DatagramSocket 构造方法:

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

DatagramSocket 方法:

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

3.2 DatagramPacket API

DatagramPacket 是UDP Socket 发送和接收的数据报.

DatagramPacket 构造方法:

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

DatagramPacket 方法:

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

构造UDP发送的数据报时 , 需要传入 SocketAddress , 该对象可使用 InetSocketAddress 来创建.


3.3 InetSocketAddress API

InetSocketAddress (SocketAddress 的子类) 构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr , int port)创建一个Socket地址 , 包含IP地址和端口号

示例一: 回显服务器(echo server)

一般服务器执行操作: 收到请求 , 根据请求计算响应 , 返回响应

echo server 省略了其中的 "根据请求计算响应" , 请求是啥就返回啥.(该服务器没有实际的业务 , 只是展示了 socket api 基本用法)

public class UdpEchoServer {
    //网络编程本质上是操作网卡
    //但网卡不方便直接操作 , 在操作系统内核中 , 使用一种特殊的叫做"socket"这样的文件来抽象表示网卡
    //因此进行网络通信势必有一个 socket 对象
    private DatagramSocket socket = null;
    //对于服务器来说,创建socket对象的同时,要给它绑定一个端口号
    //服务器是网络传输中,被动的一方,如果是操作系统的随机分配的端口,此时客户端就不知道这个端口是啥了,也就无法通信了
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("启动服务器!");
        //服务器为多个客户端服务
        while(true){
            //只要有客户端,就可以提供服务
            //1.读取客户端发来的请求
            //receive 方法的参数是一个输出形参数,需要先构造好空白的DatagramPacket对象,交给receive进行填充
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //此时这个 DatagramPacket 是一个特殊的对象,不方便直接进行处理,可以把这里包含的时刻拿出来构造成一个字符串.
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.根据请求计算响应,此处是回显服务器,请求与响应相同
            String response = process(request);
            //3.把响应数据写回到客户端,send的参数也是 DatagramPacket,需要把这个对象构造好
            //此处的响应对象不能是空的字节数组构造的而是要响应数组构造
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印一下当前请求响应的处理中间结果
            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 SocketException {
        //端口号码在 1024-65535 之间随机选择.
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = null;
    private int serverPort = 0;
    //一次通信需要有两个 ip 和两个端口
    //客户端的是 ip 127.0.0.1(环回 ip) 已知
    //客户端的端口号是系统随机分配的
    //服务器 ip 和端口号也要告诉客户端 , 才能顺利把消息发送给服务器.
    public UdpEchoClient(String serverIp , int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scan = new Scanner(System.in);
        while(true){
            //1. 从控制台读取要发送的数据
            System.out.println(">");
            String request = scan.next();
            if(request.equals("exit")){
                System.out.println("good bye");
                break;
            }
            //2. 构成 Udp 请求并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes() , request.getBytes().length,
                    InetAddress.getByName(serverIp) , serverPort);
            socket.send(requestPacket);
            //3. 读取服务器的 Udp 响应并解析.
            DatagramPacket respondPacket = new DatagramPacket(new byte[4096] , 4096);
            socket.receive(respondPacket);
            String respond = new String(respondPacket.getData() , 0 , respondPacket.getLength());
            //4. 把解析好的结果显示出来
            System.out.println(respond);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

 


示例二: 查词典服务器 

//对于UdpDictSever 主要代码与回显服务器一致
//主要是 "根据请求计算响应" 这个步骤不一样
public class UdpDictServer extends UdpEchoServer {
    private Map<String, String> dict = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);
        // 给字典设置内容
        dict.put("cat", "小猫");
        dict.put("dog", "小狗");
        dict.put("pig", "小猪");
        dict.put("mouse", "老鼠");
        // 这里可以无限的设置

    }

    @Override
    public String process(String request) {
        //查词典和过程
        return dict.getOrDefault(request, "当前单词没有查到结果");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(9090);
        server.start();
    }
}

 

 此时如果再启用回显服务 , 就会造成端口冲突(一个端口只能绑定一个进程).

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

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

相关文章

好友关注-Feed流实现方案

9.3 好友关注-Feed流实现方案 当我们关注了用户后&#xff0c;这个用户发了动态&#xff0c;那么我们应该把这些数据推送给用户&#xff0c;这个需求&#xff0c;其实我们又把他叫做Feed流&#xff0c;关注推送也叫做Feed流&#xff0c;直译为投喂。为用户持续的提供“沉浸式”…

简聊商城项目的表设计

零、前言 1、优惠卷设计 电商项目中的优惠券系统这样设计&#xff0c;同事直呼 666 &#xff01; 2、SPU和SKU的定义及他们之间的关系 SPU全称Standard Product Unit&#xff0c;即标准化产品单元。 简单理解就是某一种产品。 SKU全称Stock Keeping Unit&#xff0c;即库存量…

ChatGPT官方API可以抢先体验了

ChatGPT官方API目前还在内测当中&#xff0c;OpenAI官网上也没有任何接口介绍和文档。这对于开发和调用来说不怎么方便。但是&#xff0c;比较好的地方在于内测过程中调用是免费的&#xff0c;没有次数限制。此外&#xff0c;API接口调用不需要梯子或代理&#xff08;使用代理反…

【原创】如何做一张原创8BIT音乐的NES音乐卡片

我陷入了深思。。。。。。 第一步是创作一首8BIT音乐。我介绍两个NES用的音乐工具&#xff1a;FamiTracker 和 FamiStudio。 选FamiTracker的原因是&#xff0c;有完美教程呀。红鸡将他的教程放到B站了&#xff1a;红激教你做音乐 一共11集&#xff0c;非常亲民地道的教学&a…

C++11 lambda表达式

作者&#xff1a;小萌新 专栏&#xff1a;C进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍C11的lambda表达式 lambda表达式lambda表达式的概念lambda表达式语法lambda表达式交换两个数lambda表达式的底层原理lambda表达式的…

【ChatGPT 中文版小程序】无需注册体验 ChatGPT 的攻略

本文导读什么是ChatGPT&#xff1f;ChatGPT能做什么&#xff1f;功能测试如何解锁有趣功能&#xff1f;我想部署同样的一个小程序&#xff0c;请问如何做&#xff1f;什么是ChatGPT&#xff1f; 最近网上非常火爆的CHATGPT&#xff0c;它是OpenAI开发的一款开源的自然语言处理…

专访量子计算上市公司IonQ CEO

&#xff08;图片来源&#xff1a;网络&#xff09;IonQ的CEO Peter Chapma&#xff0c;从70年代中期就从事高科技行业&#xff0c;至今已有45年左右。在接受媒体采访时&#xff0c;Peter Chapman谈到IonQ扩展计算机和提供业务价值的计划、量子客户的最佳应用程序开发合作伙伴、…

DEFCON议题解读|Dll劫持新思路——修改环境变量

简介 在2022年的Defcon大会上,安全研究人员Wietze Beukema通过对进程级环境变量的研究&#xff0c;提出了一种Dll劫持新思路&#xff0c;下面就其中涉及的技术点展开介绍。 **01 **环境变量 每一个进程都有一个环境块&#xff0c;其中包含一组环境变量及其值。有两种类型的环…

RabbitMQ的消息模型

文章目录1、简单队列2、work 模式3、发布/订阅模式4、路由模式FanoutDirect5、主题模式6.工作模式总结7、四种交换器RabbitMQ官方提供了5个不同的Demo示例&#xff0c;对应了不同的消息模型&#xff1a; 1、简单队列 一个生产者对应一个消费者&#xff01;&#xff01; publi…

Linux shell 命令行环境下使用阿里云盘

阿里云盘在内测的时候我就在使用&#xff0c;整体体验相当的好&#xff0c;最起码不会限速&#xff0c;比起下载速度只有十几 KB 的某垃圾云盘要强太多了。 当然除了使用各系统的客户端进行下载之外&#xff0c;我还想要在命令行进行操作&#xff0c;主要原因也是我有一台 NAS…

AcWing 487. 金明的预算方案(有依赖的背包问题 + 分组背包问题)

AcWing 487. 金明的预算方案一、问题二、分析三、代码一、问题 二、分析 这道题属于一个背包问题&#xff0c;但是这道题中有一个很神奇的条件。就是我们想要购买某个物品的附件的话&#xff0c;前提是我们要购买这个物品的主件。 因此&#xff0c;我们可以将这道题画成下面这…

Java高手速成 | EL表达式语言

本文主要讲解EL表达式语言的作用、基本语法以及运算符。 01、EL的作用 当需要在JSP页面显示变量以及JavaBean对象时&#xff0c;可以使用JSP的表达式&#xff0c;如<%变量%>的形式&#xff0c;也可以直接使用如<%out.println(变量)%>的Java输出语句。尤其当JSP页…

C语言学习笔记-循环

有的时候&#xff0c;我们可能需要多次执行同一块代码。一般情况下&#xff0c;语句是按顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。 编程语言提供了更为复杂执行路径的多种控制结构。 循环语句允许我们多次执行一个…

硬核工厂!钢厂远程监管,三维组态监控 HMI

钢铁行业作为我国的支柱产业&#xff0c;也是我国能源消耗的重点行业之一&#xff0c;随着国家节能减排政策的推进&#xff0c;有效实施能源管控是企业提高能源绩效、降低能源成本和提高核心竞争力的重要途径。通过对钢铁企业能耗现状和能源管理模式的分析可以得知&#xff0c;…

GitHub中如何创建自己的存储库?(图文详解)

前言 &#x1f4dc; “ 作者 久绊A ” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 一、创建自己的存储库&#xff1f; 二、详细介绍 1、Reposito…

微前端——一个属于前端的时代

关于微前端为什么需要微前端&#xff1f;What&#xff1f;什么是微前端Why&#xff1f;为什么去使用微前端How&#xff1f;怎样落地微前端Where&#xff1f;在什么场景下使用微前端CSS 隔离方案JavaScript 沙箱机制快照沙箱Proxy 代理沙箱legacySandbox(单例沙箱)proxySandbox(…

云原生丨手把手教你搭建自己的第一个微服务

文章目录前言一、环境准备软件要求配置操作二、拉取框架三、模块搭建SDK模块SDK-Cloud 模块common模块API模块前言 我们知道&#xff0c;微服务架构是把项目里的每一个功能元素独立出来&#xff0c;再对这些功能元素进行动态组合。这样的优点在于&#xff1a;节省调用资源&…

基于Springboot搭建java项目(三十一)—— 什么是Docker

什么是Docker 一、容器技术 1、应用程序的部署问题 ​ 还原应用程序部署的场景&#xff0c;开发在开发应用的时候&#xff0c;要自己搭建一套环境&#xff0c;进行本地调试&#xff0c;这时就需要在本地搭建一套JVM&#xff0c;NODE&#xff0c;NGNIX等一些应用程序运行的环…

【大唐杯备考】——5G系统勘察设计(学习笔记)

&#x1f4d6; 前言&#xff1a;本期介绍5G系统勘察设计。 目录&#x1f552; 1. 概述&#x1f558; 1.1 5G网络预规划&#x1f564; 1.1.1 5G建网需求确认&#x1f564; 1.1.2 4G现网评估&#x1f564; 1.1.3 站点规模估算&#x1f564; 1.1.4 5G仿真评估&#x1f558; 1.2 5G…

新年首捷 | 全息网御入选《CCSIP 2022中国网络安全产业全景图(第五版)》

2023年2月1日&#xff0c;FreeBuf咨询正式发布 《CCSIP&#xff08;China Cyber Security Panorama&#xff09;2022 中国网络安全行业全景册&#xff08;第五版&#xff09;》&#xff0c;旨在帮助企业更好地了解中国网络安全技术与市场的发展趋势&#xff0c;并为企业安全建设…