当前位置: 首页 > news >正文

Java网络编程(2):(socket API编程:UDP协议的 socket API -- 回显程序)

Java网络编程(2):(socket API编程:UDP协议的 socket API – 回显程序)

文章目录

  • Java网络编程(2):(socket API编程:UDP协议的 socket API -- 回显程序)
  • 1. socket API 有两套编码方式
  • 2. UDP 的 socket API
    • 2.1 网卡 和 socket文件
    • 2.2 DatagramSocket 和 DatagramPacket和的作用
    • 2.3 DatagramSocket 提供的方法
      • 2.3.1 构造方法(打开 socket 文件)
      • 2.3.1 读写操作
      • 2.3.3 关闭 socket 文件
    • 2.4 DatagramPacket 提供的方法
      • 2.4.1 构造方法(实例化一个数据报)
      • 2.4.2 提供获取信息的方法
  • 3. 使用UDP协议的 socket API 编写服务器端程序
    • 响应和请求的介绍
    • 3.1 创建 socket 对象(DatagramSocket 对象)
    • 3.2 创建 start 方法,用来启动服务器
    • 3.3 编写一个死循环,持续读取请求数据
    • 3.4 处理请求数据(核心工作)
    • 3.5 根据三个步骤,编写服务器端程序
  • 4. 使用UDP协议的 socket API 编写客户端程序
  • 5. 启动客户端 和 服务器端,查看运行效果
    • 两个程序的运行,结合起来看
    • 存在的疑问
      • 1. 是否需要联网才能运行程序?
      • 2. 能否让你的电脑运行客户端,连接我这个电脑上的服务器?
      • 3. 遗留问题
  • 6. 完整代码:
    • 6.1 服务器代码:
    • 6.2 客户端代码:
  • 总结:

在看这篇博客之前,如果你对 socket API 有一定了解,可以直接看这篇博客。
如果没有太多了解,需要你看完这篇博客: Java网络编程(1):(使用 socket 进行网络编程前的预备知识)

注意:我们这篇博客,介绍的是如何编写服务器端的程序,所以,文章中提到的 数据报 或 数据包,我可能会写成这两种,但它们都是同一个东西,不用太纠结!!!

非要问他们有什么具体区别?
你可以看看这篇博客:Java网络初识(4):网络数据通信的基本流程 的 1.4 标题中,关于网络传输的基本数据单位的介绍。

再过来看本篇博客!

1. socket API 有两套编码方式

socket API 本身是操作系统的功能,在 JDK中,针对 socket API 进行了进一步的封装。所以,我们拿着这套封装好的 socket API 进行网络编程就可以了。

之前的博客介绍过,我们编写的程序,想要进行网络通信,就要把我们写的程序交给传输层,而我们要使用操作系统提供的 socket API让传输层接收数据完成数据传递的过程。

传输层中,有两个最核心的协议:TCP 和 UDP
这两个协议的差别是非常大的,所以,传输层给应用层提供 socket API 的时候,也是提供了两套不一样的编写代码的方式:

  1. UDP的 socket API
  2. TCP的 socket API

接下来,我们就分别来讲这两组编码方式。

本篇博客,介绍的是第一组编码方式:UDP的 socket API

2. UDP 的 socket API

UDP 的 socket API 涉及到两个最核心的类DatagramSocketDatagramPacket

2.1 网卡 和 socket文件

讲这两个类之前,我们先来铺垫一些知识。
之前 Java 文件操作我们说过,计算机中的 “ 文件 ”,通常是一个 “ 广义 ” 的概念,而针对文件IO的操作,是 “ 狭义 ” 的概念,指的是硬盘上的文件
除此之外,文件,还可以代指一些硬件设备,换句话说:操作系统管理硬件设备,也是抽象成文件,统一管理的

那么,我们网络通信会用到什么硬件设备
答:网卡

网卡在实际中,又有 有线网卡和无线网卡。
有线网卡,一般是集成在主板上的,不好观察。
无线网卡,一般是独立出来的,比较明显。
在这里插入图片描述

网卡的功能和作用如下:
网卡作为计算机与计算机间进行通信的桥梁,主要有以下两大功能:
一、是读入由网络设备传输过来的数据包,经过拆包,将其变成计算机可以识别的数据,并将数据传输到所需设备中
二、是将计算机发送的数据,打包后输送到其他网络设备中

那么,网卡这个硬件设备,在操作系统中,就被抽象成 “ socket文件 ”,既然是抽象成文件,那么,我们在操作网卡的时候,就是在操作 socket文件 ,操作的过程,和操作普通文件差不多,都是:打开 —> 读写 —> 关闭

为什么会有这样的设定?
答:网卡是个硬件设备,直接操作网卡,不好操作,所以把操作网卡转换成操作 socket 文件,socket 文件就相当于 “ 网卡的遥控器 ”

DatagramSocket 这个类,可以当作是用来表示网卡的文件,通过它,来操作网卡,前面加的 “ Datagram ”,表示它通过UDP这个协议来进行网络通信。

2.2 DatagramSocket 和 DatagramPacket和的作用

这两个类的作用,是什么?
DatagramSocket :是 UDP Socket,用于发送和接收UDP数据包,它是用来操作网卡的。
DatagramPacket :是 UDP Socket 发送和接收的数据包,它是用来创建(实例化)一个完整的UDP数据包的。

2.3 DatagramSocket 提供的方法

2.3.1 构造方法(打开 socket 文件)

注意:我们这里这个表格显示的构造方法,不是全部的构造方法,不完整!!!

这里提供的构造方法,构造方法的作用:相当于打开文件(socket文件)
打开文件,是读写网络数据的前提条件。

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

表格中的第二个方法,int port ,指的是端口号,需要手动指定。
当我们创建 socket 的时候,传入一个端口号,就会关联上一个端口号

端口号的作用:使用端口号,来区分同一台计算机(主机)上不同的应用程序
换句话说:只要你这个程序,需要操作网络,在这之前,需要你把这个程序本身,和一个端口号建立关联关系,而且这个端口号,不能和其他的程序所关联的端口号相同,也就是,一个端口号不能重复使用

DatagramSocket(),这个构造方法,会随机分配一个端口(常用于客户端)。
DatagramSocket(int port),带有参数的构造方法,要手动指定一个端口(常用于服务器端)。
为什么一个常用于客户端?一个常用于服务器端?
编写 客户端 程序的时候,会解释到。

为什么要有端口?
可以看这篇博客:Java网络初识(2):IP地址和端口号,协议,五元组

2.3.1 读写操作

这里提供读写操作的方法,和文件操作中的read 和 write 不一样,这里提供的是 receive接收,相当于读取数据) 和 send发送,相当于写数据)。

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

从以上两个方法的参数我们可以知道,参数都是 DatagramPacket 这个类实例化的对象,也就是说,我们进行网络通信的时候,是通过 DatagramPacket 这个类,实例化出一个一个的数据报以数据报为单位,进行接收和发送。

2.3.3 关闭 socket 文件

方法名方法说明
void close()关闭此数据报套接字

2.4 DatagramPacket 提供的方法

2.4.1 构造方法(实例化一个数据报)

注意:我们这里这个表格显示的构造方法,不是全部的构造方法,不完整!!!

通过 DatagramPacket 这个类提供的构造方法,我们可以实例化出一个完整的数据报。

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

UDP 数据包的载荷数据,就可以通过构造方法中的byte[] buf来指定并存储。

2.4.2 提供获取信息的方法

不论是服务器端还是客户端,通过 DatagramPacket 类实例化出的对象(一个数据包),我们总会获取到这个对象(数据包)的某些信息,如:

  1. 发送端主机IP地址
  2. 接收端主机IP地址
  3. 发送端主机的端口号
  4. 接收端主机端口号
  5. 数据报中的数据(字节数组里存着的信息)

为了获取这些信息,DatagramPacket 这个类,也提供了一些方法,让我们可以获取到以上这些信息。

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

3. 使用UDP协议的 socket API 编写服务器端程序

这里编写的是 UDP协议 下,编写一个服务器端的网络编程代码,这个代码中,会用到第二个大标题中,介绍到的所有类和类提供的方法。

创建过程:打开 IDEA —> 新建项目 —> 在 src 目录下创建一个包(network包)—> 包里面,创建两个类:UdpEchoServer 和 UdpEchoClient。
解释一下这两个类的类名的意思:
Server:这个单词的中文意思:服务器
Cilent:这个单词的中文意思:客户端
Echo:这个单词的中文意思:回声,回显

EchoServer 就叫做回显服务器,什么意思?

响应和请求的介绍

讲解意思之前,还需要介绍一下请求和响应:
请求客户端 给 服务器端 发送一个数据,叫做请求
响应服务器端 返回给 客户端 一个数据,叫做响应
回显服务器的意思就是说:请求是什么,响应就是什么
比如:请求是 “ hello ” 这么一个字符串,响应同样是 “ hello ” 。

所以,我们这里设计的程序,就是要达成这样的效果!
但是真实情况中,真正的服务器,请求和响应肯定是不一样的我们这里,不去理会服务器的复杂逻辑,主要是学习 UDP 的 socket API 的初步使用,因此,这里就写一个最简单的回显。

回显类似于什么呢?
有点像鹦鹉学舌,就你说什么,鹦鹉就说什么,模仿你说话。

3.1 创建 socket 对象(DatagramSocket 对象)

创建 socket对象,为的是调用 DatagramSocket 提供的方法。

在这里插入图片描述
图中可以看到:

  1. 关于网络编程的类,都在 net 包里面
  2. DatagramSocket 这个类的构造方法,需要处理 SocketException 这个异常,抛出到方法处即可
  3. UdpEchoServer类的构造方法初始化socket对象,初始化的时候,指定了一个固定的端口号,让服务器来使用

经过以上三步,我们就创建好了一个 socket对象。

3.2 创建 start 方法,用来启动服务器

在这里插入图片描述

3.3 编写一个死循环,持续读取请求数据

对于服务器来说,客户端什么时候发请求发送多少个请求,我们是无法预测的。
例如:
你玩游戏(手机里的游戏APP,就是客户端),游戏对应的服务器端(也就是服务器),是不知道你什么时候登录游戏,发送一个登录游戏的请求的,也不知道,在一个小时内,有多少个玩家发送了多少个登录请求,游戏公司无法预测,所以,游戏的服务器,是 7 X 24 小时,不断工作的,只有游戏版本更新,系统维护的时候,服务器才会关闭。
服务器关闭了,玩家登录游戏的时候,发送的登录请求,服务器关闭了,无法作出响应,你就登录不了游戏了。
只有更新结束,游戏系统维护好了以后,才能正常玩游戏。

通过上述例子可知:服务器端程序(服务器)通常都需要有一个 死循环,持续不断的尝试读取客户端的请求数据,做到 7 X 24小时运行。
在这里插入图片描述
进入循环后,意味着服务器要开始不断的处理客户端的请求数据了。

3.4 处理请求数据(核心工作)

处理请求的过程,典型的服务器都是分为三个步骤的,分别是:

  1. 读取请求,并解析请求数据
  2. 根据请求,计算响应(服务器最关键的逻辑

本身服务器就是要提供各种各样的功能的,比如:百度搜索 “ 如何学习好Java ”,我的请求是:“ 如何学习好Java ”,服务器端的程序就会根据请求,搜索出很多个相关的页面。

说起来很简短,但是,在服务器端程序上,会经过各种各样的逻辑处理,处理请求,计算响应,最终返回结果。
所以,这一块是服务器最关键的逻辑。

但是,我们这篇博客编写的是回显服务器,所以,这个环节就相当于省略了,请求是什么,我们就返回什么

  1. 把响应返回给客户端

所以,一个典型的服务器,都是按照这三个步骤来进行处理的,不仅是我们编写的这个回显服务器,未来,你从事编写服务器端程序(服务器)相关的工作岗位,涉及到更加复杂的,商业级别的服务器,总体上的逻辑,也就是这三步。
只不过,公司的服务器端程序,会考虑的更多,支持的功能会更多,会很复杂,上至几十万行的代码量。

所以,清楚了这三步基本的步骤,我们就可以开始真正编写服务器了。

3.5 根据三个步骤,编写服务器端程序

由于本篇博客,加上这第五步的全部内容,达到 14000+ 字数,所以,我单独写到了另一篇博客当中:
Java网络编程:(socket API编程:UDP协议的 socket API – 服务器端程序的编写)
大家点进去,看完这篇博客,再回来看这篇博客!

4. 使用UDP协议的 socket API 编写客户端程序

同样,为了避免本篇博客字数太多,编写客户端的介绍,我也分离出了一篇博客出来:
Java网络编程:(socket API编程:UDP协议的 socket API – 客户端程序的编写)
大家点进去,看完这篇博客,再回来看这篇博客!

5. 启动客户端 和 服务器端,查看运行效果

客户端运行效果:
在这里插入图片描述
服务器端运行效果:
在这里插入图片描述
这就是一整个回显程序的运行效果了。

两个程序的运行,结合起来看

在这里插入图片描述

  1. 客户端发送请求数据,服务器端当前处于 阻塞等待
  2. 服务器端接收请求数据包,并解析
  3. 计算响应后,返回响应数据给 客户端
  4. 客户端接收响应数据

所以,客户端 和 服务器端 要相互配合,才能完成一个网络数据请求的工作。

存在的疑问

1. 是否需要联网才能运行程序?

这个回显程序,是否需要我们联网才能运行呢?
答:如果是在 同一个 主机上,就不需要联网,也能运行程序。
如果是 不同 主机的话,就需要连接网络了。

2. 能否让你的电脑运行客户端,连接我这个电脑上的服务器?

你希望,用你自己的电脑,运行我刚才实现的客户端程序,同时连接我的服务器,你发送请求,我这边响应数据,这样的方式,能不能实现?

答:可以,但是又不可以!!!
原因:想要实现这样的方式,有很多限制,比如:你电脑要和我连接同一个 WiFi 才可以,即咱们两个要处于同一个局域网

如果你连接的是你家里的 / 学校的,就不可以连接我的这个服务器。

为啥要这样?原因后面博客会讲解到(NAT机制)。
NAT机制(网络地址映射),导致我们要处于同一个局域网,才能实现:你运行客户端,连接我的服务器端。
但是,我们还可以用另一种方式解决:云服务器
我可以把程序部署到云服务器上,你就可以访问到云服务器,连接到服务器了。

那么,这个操作,后续会讲到。

3. 遗留问题

  1. NAT机制(网络地址映射)
  2. 部署云服务器(网络不涉及太深,当你学习Java框架,部署项目的时候,就会了解到)

6. 完整代码:

6.1 服务器代码:

以下,是整个服务器端程序,完整的代码:

/*** Created with IntelliJ IDEA.* Description:服务器端程序* Date: 2025-04-28* Time: 20:48*/
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket = null;//    UdpEchoServer类的构造方法,初始化socket对象public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {
//        启动服务器System.out.println("服务器启动");//        服务器要持续不断的读取客户端的请求数据,要有一个死循环while(true){
//            循环一次,就相当于处理一次请求
//            处理请求的过程,典型的服务器都是分为三个步骤的,分别是:
//            1. 读取请求,并解析请求数据
//            此处创建的 DatagramPacket对象,表示一个 UDP数据报,
//            此处传入的字节数组,就保存了 UDP数据报 的载荷部分DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
//            读取请求:socket.receive(requestPacket);
//            把读取到的二进制数据,转换成字符串String request = new String(requestPacket.getData(),0, requestPacket.getLength());
//            解释:1.requestpacket.getData():获取到请求数据
//                 2.  0,表示从字符串的 0 下标开始存放数据,
//                 3. requestpacket.getLength():获取到requestpacket中,有效的数据的长度//            2. 根据请求,计算响应(服务器最关键的逻辑)
//            回显服务器,请求是什么,响应就是什么String response = process(request);//            3. 把响应返回给客户端
//            根据 response 构造一个新的  DatagramPacket 对象,将这个对象返回给客户端DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());//            将创建好的DatagramPacket 对象(响应数据报)发送给客户端socket.send(responsePacket);//            4.打印日志System.out.printf("[%s:%d] req(请求): %s,  resp(响应): %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
//            requestPacket.getAddress().toString():获取请求方(客户端)的IP地址并以字符串的方式进行打印
//            requestPacket.getPort():获取请求方(客户端)的端口号}}private String process(String request) {return request;}// 编写main方法,启动服务器public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}}

6.2 客户端代码:

以下,是整个客户端程序的完整代码:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;/*** Created with IntelliJ IDEA.* Description:客户端程序* Date: 2025-04-28* Time: 20:48*/
public class UdpEchoCilent {private DatagramSocket socket = null;
//    由于 UDP 协议本身,并不会保存对端的信息,我们需要在自己的代码中保存一下private String serverIP;private int serverPort;
//客户端和服务器端不一样,
//客户端这里的构造方法,是要指定访问的服务器的地址(IP 和 端口号)public UdpEchoCilent(String serverIP,int serverPort) throws SocketException {this.serverIP = serverIP;this.serverPort = serverPort;socket = new DatagramSocket();}public void start() throws IOException {
//        启动客户端程序//        客户端不断工作,发送请求数据while(true) {
//        1.从控制台读取用户输入的内容Scanner scanner = new Scanner(System.in);System.out.println("请输入要发送内容:");String request = scanner.next();//        2.把请求发送给服务器,需要构造 DatagramPacket 对象 ,将请求封装成数据包,再进行发送
//        构造过程中,不光要构造载荷(实际传输的用户数据),还要设置 服务器IP地址 和 端口号DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIP),serverPort);//        3.发送数据包socket.send(requestPacket);//        4.接收服务器的响应内容DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//        5.从服务器读取的数据,进行解析,并打印出来(将返回的响应数据,解析并打印)String response = new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println("服务器返回的响应数据:"+response);}}public static void main(String[] args) throws IOException {UdpEchoCilent cilent = new UdpEchoCilent("127.0.0.1",9090);cilent.start();}}

总结:

本篇博客,我们主要是使用了 UDP协议的 socket API,来实现了回显程序(请求是什么,响应就是什么),虽然这个回显,在以后的工作中,不会见到。

但是,我们通过这个程序,也知道了网络编程,是怎么编程的,中间要做的事情有哪些,这些编程的思想,才是最重要的。

最后,如果看完这篇博客,你有所收获,请给我点点赞,如果这篇博客有错误的地方,或者你有什么意见,也欢迎指出!


文章转载自:

http://RtnrmPrZ.jtmqL.cn
http://nQlrCuh2.jtmqL.cn
http://2zorImMH.jtmqL.cn
http://c2YF8zcg.jtmqL.cn
http://1WiHrzIB.jtmqL.cn
http://HUX5xp0C.jtmqL.cn
http://j19LH3sm.jtmqL.cn
http://buWMeq3M.jtmqL.cn
http://3KRVqJgO.jtmqL.cn
http://yA6dVY03.jtmqL.cn
http://jpkJU8zS.jtmqL.cn
http://x9uTyUBe.jtmqL.cn
http://aN18via0.jtmqL.cn
http://OfyJZ9JO.jtmqL.cn
http://BITRljXO.jtmqL.cn
http://92n7G6Cb.jtmqL.cn
http://z7LqwG3o.jtmqL.cn
http://ak6sS0ME.jtmqL.cn
http://n5VHl9c2.jtmqL.cn
http://ddPJhg48.jtmqL.cn
http://Ef15mBOa.jtmqL.cn
http://RJfvdWhH.jtmqL.cn
http://jab0ppcc.jtmqL.cn
http://ZDmsYn6s.jtmqL.cn
http://DpukO12m.jtmqL.cn
http://ZTEU5Rq6.jtmqL.cn
http://vbZH0Xzi.jtmqL.cn
http://afzUqsov.jtmqL.cn
http://PC33pMjC.jtmqL.cn
http://V8ePWdxC.jtmqL.cn
http://www.dtcms.com/a/380263.html

相关文章:

  • Java 类加载机制双亲委派与自定义类加载器
  • OpenLayers数据源集成 -- 章节九:必应地图集成详解
  • 前端调试工具有哪些?常用前端调试工具推荐、前端调试工具对比与最佳实践
  • 【C++练习】16.C++将一个十进制转换为二进制
  • 公司本地服务器上搭建部署的办公系统web项目网站,怎么让外网访问?有无公网IP下的2种通用方法教程
  • 【C++】string类 模拟实现
  • 【系列文章】Linux中的并发与竞争[02]-原子操作
  • 微信小程序 -开发邮箱注册验证功能
  • 使用ollama启动文心开源大模型0.3b版本
  • 【langchain】构建检索问答链
  • QT M/V架构开发实战:QSqlQueryModel/ QSqlTableModel/ QSqlRelationalTableModel介绍
  • 网络编程入门:构建你的第一个客户端-服务器应用
  • 极简灰度发布实现新老风控系统切流
  • 基于跳跃表的zset实现解析(lua版)
  • 【学习K230-例程18】GT6700-HTTP-Server
  • Redis列表(List):实现队列/栈的利器,底层原理与实战
  • 超级流水线和标量流水线的原理
  • 漫谈《数字图像处理》之边缘检测与边界预处理的辨析
  • (二)文件管理-文件查看-less命令的使用
  • 深入理解节流(Throttle):原理、实现与应用场景
  • 汽车电子电气架构中的电源架构(下)
  • GISBox与GeoServer使用体验全对比:轻量化工具如何重新定义GIS价值?
  • 02.【Linux系统编程】Linux权限(root超级用户和普通用户、创建普通用户、sudo短暂提权、权限概念、权限修改、粘滞位)
  • JavaEE 初阶第二十二期:网络原理,底层框架的“通关密码”(二)
  • Netty 实战应用:从 RPC 到即时通讯,再到 WebSocket
  • 南京方言数据集|300小时高质量自然对话音频|专业录音棚采集|方言语音识别模型训练|情感计算研究|方言保护文化遗产数字化|语音情感识别|方言对话系统开发
  • Django全栈班v1.04 Python基础语法 20250912 下午
  • uniapp多端打包样式处理
  • Unity学习----【进阶】TextMeshPro学习(一)--基础知识点
  • Echarts雷达图根据数值确定颜色