Java 网络编程(一)—— UDP数据报套接字编程

news2024/11/27 12:54:11

概念

在网络编程中主要的对象有两个:客户端和服务器。客户端是提供请求的,归用户使用,发送的请求会被服务器接收,服务器根据请求做出响应,然后再将响应的数据包返回给客户端。

作为程序员,我们主要关心应用层和传输层,我们编写的程序属于应用层,需要调用传输层的接口来进行数据的传输。Java给我们提供了两套接口,一套是属于UDP 协议的,另一套是属于 TCP 协议的。本篇文章重点讲解UDP 数据报套接字编程。

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

UDP数据报套接字

DatagramSocket

DatagramSocket 简单来理解就是定位你所在的位置,用于接收和发送数据报

构造方法:

方法名说明
DatagramSocket()无参构造方法,不指定端口号,由操作系统自行分配
DatagramSocket(int port)port 就是端口号,这个构造方法就是由程序员自行指定端口号

接收和发送数据包的方法:

方法名说明
send(DatagramPacket p)发送数据包
receive(DatagramPacket p接收数据包,这里是输出型参数,传输层把数据内容填充到你传入的数据包中

什么是输出型参数?
该参数在方法内部会被改变,并且会影响到方法外部的实参。

关闭方法:

方法名说明
close()关闭资源

注意了网络编程使用 Socket ,也是和内存、文件一样都会消耗资源的。

DatagramPacket

DatagramPacket 就是数据报,也就是你发送和接收的数据报,这里数据报和数据包不作区分,大家知道就好了。

构造方法:

字节数组就是用来填充数据的,也是输出型参数。
offset 是指定偏移量
length 是指定要填充多少个字节
address 就是传入地址,SocketAddress 就是一个完整的地址(包含IP 和端口号),InetAddress 只是包含 IP地址,port 就是我们熟悉的端口号。

其他方法:

方法名说明返回值
getAddress()获得该数据包的 IP 地址InetAddress
getPort()获得该数据包端口号int
getSocketAddress()获得该数据包的完整地址(包含IP地址和端口号)SocketAddress
getData()或者数据内容byte[]
getLength()获得数据的长度,以字节为单位int

InetSocketAddress

构造方法:

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

其他方法:

方法名说明注意
getByName(String host)将主机名转化为机器能识别的IP地址静态方法

这个方法有什么用?
我们知道一个IP地址我们习惯用十进制来表示,类似”xxx.xxx.xxx.xxx",我们通常传入的这个IP地址是一个字符串,这个方法就能将这个字符串转化为机器能识别的二进制的 IP 地址。

回显服务器编写

这里简单介绍一下,回显服务器就是你发什么我就回什么,例如客户端发送一个 hello,服务器直接返回 hello,这就是回显服务器,此服务器是用来我们学习套接字的。现在我来带领大家完成服务器代码的编写。

首先创建一个服务器的类,这里定义为 UdpEchoServer,Echo 就是回显的意思。
在类里面先定义字段 DatagramSocket socket

    private DatagramSocket socket;

    //给服务器指定一个端口号
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

在构造方法这里要指定对应的端口号,服务器的位置一定要固定下来,防止客户端那边找不到服务器。


启动程序

服务器是 7 * 24 小时为用户提供的服务的,所以这里我们直接写一个死循环 while(true) {}
每一次循环都是在处理一次请求。

首先我们要接收客户端的数据包,先创建好一个空的数据包来接收数据,这里为什么不传入地址,因为我们这个数据包只是用来接收数据的,并且就在服务器中使用,不需要添加地址。

//构建请求数据包
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);

然后接收:

//获取请求
socket.receive(requestPacket);//输出型参数

如果没有数据可以接收的话,服务器程序会一直在这里阻塞住。

解析数据包并计算请求,由于这里是回显服务器,所以我们直接构造出 String,然后形式上进行响应的处理:

//解析请求数据包
String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

//计算响应值
//这里是回显服务器,直接返回原数据
String response = process(request);
    //计算响应,服务器的核心代码区域
    private String process(String request) {
        return request;
    }

在真实的服务器代码中,我们在响应这里的处理是服务器的核心逻辑,由于是回显服务器,也就显得没有什么感受。


之后就要把响应的数据包发送回客户端那边。

注意:由于 UDP 是不会保存对端的 IP地址和端口号的,所以我们在构建响应数据包的时候,一定要传入目的 IP 和 目的端口号,这里的目的 IP 和 目的端口号可以从请求的数据包获取,因为请求的数据包保存了客户端的IP 和 端口号。

//构建响应数据包
//UDP 不存放对端的源 IP 和 源端口,所以需要传入对方的地址
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
requestPacket.getSocketAddress());

//发送响应数据包
socket.send(responsePacket);

还要注意数据的长度一定是response.getBytes().length,不要写出字符串的长度,因为我们的数据是字节,字节的大小和字符的大小是不一样的。


最后我们可以打印一个日志:

//打印日志
System.out.printf("[%s : %d] request: %s response: %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);

因为 getAddress() 的返回值是 InetAddress ,所以要使用 toString() 转化为字符串进行打印。

最终代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

//服务器程序
public class UdpEchoServer {
    private DatagramSocket socket;

    //给服务器指定一个端口号
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    //服务器启动运行程序
    public void start() throws IOException {
        System.out.println("服务器启动...");

        //服务器持续运行
        //每次循环处理一次请求
        while(true) {
            //构建请求数据包
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            //获取请求
            socket.receive(requestPacket);//输出型参数

            //解析请求数据包
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());

            //计算响应值
            //这里是回显服务器,直接返回原数据
            String response = process(request);

            //构建响应数据包
            //UDP 不存放对端的源IP 和 源端口,所以需要传入对方的地址
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            //发送响应数据包
            socket.send(responsePacket);

            //打印日志
            System.out.printf("[%s : %d] request: %s response: %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }

    //计算响应,服务器的核心代码区域
    private String process(String request) {
        return request;
    }

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

客户端编写

客户端是一定要知道请求是发到哪一个服务器上的,所以我们需要保存好服务器的IP 地址和端口号。

private DatagramSocket socket;
private String serverIP;
private int serverPort;

构造方法:注意一定要传入服务器的 IP地址 和 端口号,socket 使用的是 DatagramScoket 的无参构造方法。

为什么使用的是 DatagramScoket 的无参构造方法?
首先我们作为程序员不知道用户那边的主机的端口使用情况,如果固定用户的端口号,正好用户此时已经有进程占用了这个端口号,这时候我们的客户端程序是跑不起来的,这就是端口冲突。
为了避免端口的冲突,我们不指定端口号,而是交给用户主机的操作系统自行指定端口号。

    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        this.serverPort = serverPort;
        this.serverIP = serverIP;
        socket = new DatagramSocket();//不用指定客户端的端口号,让用户自己的操作系统自己去安排端口号,避免端口号冲突
    }

启动程序

这里我们直接让用户从控制台输入要发送的数据,我们构建好请求数据包并发送到服务器上

//构建请求数据包
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIP),serverPort);

//发送数据包
socket.send(requestPacket);

这里我们就使用了InetAddress.getByName()这个方法将本身是 String 类型的IP地址转化为机器能识别的IP地址。
然后要注意数据的长度一定是request.getBytes().length,不要写出字符串的长度,因为我们的数据是字节,字节的大小和字符的大小是不一样的。

最后就是传入目的地址也就是服务器的源IP和源端口号,然后发送给服务器那边。


接着就是接收服务器的响应,我们构建一个空的响应数据包来接收:

//接收数据包
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(responsePacket);

最后就是解析并打印响应内容了。

//打印响应数据
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println("响应:" + response);

最终代码

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

//客户端程序
public class UdpEchoClient {
    private DatagramSocket socket;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        this.serverPort = serverPort;
        this.serverIP = serverIP;
        socket = new DatagramSocket();//不用指定客户端的端口号,让用户自己的操作系统自己去安排端口号,避免端口号冲突
    }

    public void start() throws IOException {
        System.out.println("欢迎来到客户端...");
        Scanner scan = new Scanner(System.in);
        while(true) {
            //用户从控制台输入数据
            System.out.println("请输入你要发送的数据:");
            while(!scan.hasNext()) {
                break;
            }
            String request = scan.nextLine();

            //构建请求数据包
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);

            //发送数据包
            socket.send(requestPacket);

            //接收数据包
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);

            //打印响应数据
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println("响应:" + response);
        }
    }

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

由于我这里是使用一台主机的两个进程来模拟客户端和服务器的,所以IP地址指定为"127.0.0.1",这是每台主机默认的IP地址,端口号这里指定为 9090,最后大家运行两个程序,就可以看到下面的效果了。

效果展示:

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Jmeter命令监控CPU等指标

JMeter 命令行执行脚本得到的报告中,是没有CPU、内存使用率等监控数据的,但是可以使用JMeter插件帮忙。 一、下载jmeter-plugins-manager.jar 下载后将文件放到jmeter安装包lib/ext目录下。打开Jmeter》菜单栏》选项》Plugins Manager 二、安装PerfMon…

ES + SkyWalking + Spring Boot:日志分析与服务监控(三)

目录 一、搭建SkyWalking 1.1 版本选择 1.2 下载安装 1.3 配置启动 1.4 SkyWalking UI介绍 二、Springboot项目使用 2.1 Agent下载 2.2 Agent配置skywalking oap地址 2.3 IDEA配置Agent地址 2.4 生成的ES索引介绍 三、在kibana上查看日志 四、问题和解决 3.1 日志…

如何快速搭建一个spring boot项目

一、准备工作 1.1 安装JDK:确保计算机上已安装Java Development Kit (JDK) 8或更高版本、并配置了环境变量 1.2 安装Maven:下载并安装Maven构建工具,这是Spring Boot官方推荐的构建工具。 1.3 安装代码编辑器:这里推荐使用Inte…

spring-第十三章 AOP

spring 文章目录 spring前言1.AOP介绍2.AOP七大术语3.切点表达式4.使用spring的AOP4.1概述4.2准备工作4.3基于注解方式使用AOP4.3.1准备目标类和目标方法4.3.2编写配置类4.3.3编写通知类4.3.4编写测试类4.3.5通知类型4.3.6切面的先后顺序4.3.7PointCut注解通用切点 4.4基于XML方…

jmeter常用配置元件介绍总结之安装插件

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之取样器 jmeter常用配置元件介绍总结之安装插件 1.下载插件2.安装插件管理包3.不用插件管理包,直接官网插件下载安装 1.下载插件 jm…

MySQL 多数据库备份与恢复,包括查询,函数,SP

一、备份语句: mysqldump 可以用来导出单个数据库、多个数据库,甚至所有数据库的数据。以下是导出多个数据库到指定文件位置的语法和具体案例。 基本语法 mysqldump -u [username] -p[password] --databases [db1] [db2] ... > [file_path] -u: …

contenteditable实现需要一个像文本域一样的可编辑框

我这里是因为左上和右下有一个固定的模板,所有用textarea有点不方便,查了下还有一个方法可以解决就是在需要编辑的元素上加上 :contenteditable"true" 完整代码如下,因为这个弹窗是两用的,所以用messageType做了一下判…

linux 安装anaconda3

1.下载 使用repo镜像网址下载对应安装包 右击获取下载地址,使用终端下载 wget https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Linux-x86_64.sh2.安装 使用以下命令可直接指定位置 bash Anaconda3-2024.02-1-Linux-x86_64.sh -b -p /home/anaconda3也…

如何选择适合的AWS EC2实例类型

在云计算的世界中,Amazon Web Services(AWS)提供了丰富的服务,其中Elastic Compute Cloud(EC2)是最受欢迎的服务之一。选择合适的EC2实例类型对于确保应用程序的性能和成本效益至关重要。我们九河云通过本文…

(蓝桥杯C/C++)——基础算法(下)

目录 一、时空复杂度 1.时间复杂度 2.空间复杂度 3.分析技巧 4.代码示例 二、递归 1.递归的介绍 2.递归如何实现 3.递归和循环的比较 4.代码示例 三、差分 1.差分的原理和特点 2.差分的实现 3.例题讲解 四、枚举 1.枚举算法介绍 2.解空间的类型 3. 循环枚举解…

7.5 inch电力线载波通信技术

7.5寸电子桌牌 产品型号 PE75R_D_W 尺寸 176.2*137.15*80mm 屏幕尺寸 7.5 inch 显示区域(mm) 163.2(H) * 97.92(V) 分辨率 800*480 显示技术 电子墨水屏双面显示 显示颜色 黑/白/红 外观颜色 银色 工作温度 0-40℃ 视角 180 支持内容格式 文本/图片/二维码…

Linux下的ADC

ADC ADC简介 ADC是 Analog Digital Converter 的缩写,翻译过来为模数转换器,ADC可以将模拟值转换成数字值。模拟值是什么呢?比如我们日常生活中的温度,速度,湿度等等都是模拟值。所以如果我们想测量这些模拟值的值是多少&#x…

星空天文 2.0.1| 完全免费的观星软件,无注册登录,天文爱好者必备。

星空天文是一款完全免费且功能强大的观星软件,适用于安卓平台。无需注册登录即可使用,界面设计精美且操作简单。软件支持AR实景模式,可以将实景与星空结合,增强观星体验。用户可以设定任意日期和时间来观察不同时段的天空&#xf…

书生大模型实战营第四期-入门岛-1. Linux前置基础

入门岛-Linux前置基础 书生大模型实战营-第四期-Linux前置基础: 任务:https://github.com/InternLM/Tutorial/blob/camp4/docs/L0/linux/task.md 文档:https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/linux 任务描述完成所需时…

JavaEE初阶--servlet篇(三)HttpServlet/response/request对应方法使用

文章目录 1.总括说明2.httpservlet父类2.1方法介绍2.2dopost方法的演示2.3doput方法的演示 3.HttpServletRequest类3.1方法说明3.2方法使用演示3.3getparameter方法使用3.4使用form表单的方式3.5jackson获取参数 4.HttpResponse类4.1设置状态码4.2自动进行刷新4.3重定向跳转4.3…

前后端分离,Jackson,Long精度丢失

案例:后端接口放回一个Long数据 GetMapping("/testForLong")public Map<String, Object> testForLong() {Map<String, Object> map new HashMap<>();map.put("aaa", 1234567890123456789L);return map;}实际前端接收的数据 前后端数据…

记某单位众测项目漏洞挖掘中的一些手法

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 一个想当文人的黑客 &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【edusrc漏洞挖掘】 【VulnHub靶场复现】【面试分析】 &#x1f389;欢迎…

用 Python 写了一个天天酷跑(附源码)

Hello&#xff0c;大家好&#xff0c;给大家说一下&#xff0c;我要开始装逼了 这期写个天天酷跑玩一下叭&#xff01; 制作一个完整的“天天酷跑”游戏涉及很多方面&#xff0c;包括图形渲染、物理引擎、用户输入处理、游戏逻辑等。由于Python是一种高级编程语言&#xff0c;…

芯片设计公司ERP系统如何实现一体化管理

在当今高科技迅猛发展的时代&#xff0c;芯片设计行业作为信息技术的核心&#xff0c;正面临着日益激烈的市场竞争和复杂多变的市场需求。为了提升企业的运营效率和市场竞争力&#xff0c;芯片设计公司纷纷引入ERP(企业资源计划)系统&#xff0c;以实现一体化管理。接下来我们跟…

50岁+人群月活超1亿,短剧迎来新对手,小程序游戏“收割”中老年

抢夺中老年流量&#xff1a;微短剧向左&#xff0c;小游戏向右 作者&#xff5c;AgeClub 干货抢先看 1.《黑神话&#xff1a;悟空》走红&#xff0c;吸引大量玩家入坑单机市场。与硬核单机游戏不同&#xff0c;在渗透率更高的小游戏领域&#xff0c;聚集了更多“网瘾”中老年…