【Java网络编程】基于UDP-Socket 实现客户端、服务器通信

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

哈喽大家好~我是你们的老朋友保护小周ღ  


本期为大家带来的是网络编程的 UDP Socket 套接字基于 UDP协议的 Socket 实现客户端服务器通信Socket 套接字可以理解为是传输层给应用层提供的一组 API如此程序确定不来看看嘛~~


本期收录于博主的专栏JavaEE_保护小周ღ的博客-CSDN博客

适用于编程初学者感兴趣的朋友们可以订阅查看其它 “JavaEE基础知识”。
更多精彩敬请期待保护小周ღ *★,°*:.☆(▽)/$:*.°★* ‘


Socket 套接字可以理解为是操作系统提供给程序员的一组用于网络编程的API 接口——传输层的接口传输层给应用层提供的一组 API统称为 Socket API 。网络通信的底层逻辑都已经被操作系统封装好了开发人员就可以根据接口开发实现网络通信。

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

  • 字节流套接字使用传输层TCP协议
  • 数据报套接字使用传输层UDP协议
  • 原始套接字用于自定义传输层协议

 本篇博客主要讲述面向数据报的网络编程。


一、Java基于UDP数据报套接字通信模型

UDP 协议的特点

  1. 面向无连接传输数据之前通信双方不依赖于建立连接只需要知道谁发给谁即可。
  2. 不可靠传输只负责发送不关注数据是否传输成功即使没发送成功啥也不干也没有反馈
  3. 面向数据报使用 UDP 数据报的形式传输数据报可以理解为数据是一块一块的传输。
  4. 有缓存区的概念接收缓冲区发送缓冲区在进行网络数据通信时用于存储待发送或已接收数据的缓冲区。
  5. 数据传输大小受限制一个UDP 数据报最大占 64k, 1k = 1024字节
  6. 全双工通信通信双方都可以同时进行信息交互

总结UDP 协议具有面向无连接面向数据报特点即使通信双方没有建立连接也会传输数据并且一次性发送全部的数据报一次性接受全部的数据报。

Java 中基于UDP协议通信主要使用 DatagramSocket 类来创建数据报套接字并使用
DatagramPacket 作为发送或接收的UDP数据报。


针对一个客户端对服务器发出一次请求服务器针对该请求给予响应流程如下

在真实的网络环境中一个服务器往往会给多个客户端提供响应。

举个实际的例子根据以上流程图理解一下张三老铁使用手机百度浏览器搜索 “美女图片”此时前端页面就会将这条搜索记录 “美女图片” 拿到后台打包成UDP 数据报以请求的形式发给百度服务器百度服务器拿到请求后解析数据报查看哦~ 要美女啊我这有很多于是将“本地”存储的美女图片作为 UDP 数据报的形式响应给请求端百度浏览器拿到百度服务器响应的数据后通过解析将图片展示在张三的手机界面上。


二、UDP 数据报套接字编程

2.1  DatagramSocket API

DatagramSocket 是Socket 套接字基于UDP 协议来发送和接受数据报的类.

Datagram : 数据报

Socket : 说明这个对象是一个 Socket 对象

Socket 对象相当于操作系统一个特殊的文件这个文件并非对应对应的硬盘上的某个数据存储区域而是对应到网卡这个硬件

往 socket 对象中写数据相当于通过网卡发送消息

从 socket 对象中读数据相当于通过网卡接收消息

所以想要进行网络编程使得两个设备相互通信就需要有 socket 文件这样的对象借助这个 socket 文件对象来间接的操作网卡操作系统基本设计思想一切皆文件为了简化系统内核的逻辑设计socket 对象面向字节流读写


此处Socket 对象可以被客户端 / 服务端使用服务器这边的 Socket 必须要关联一个具体的端口号每个联网的设备在启动时或随机或指定都会绑定一个端口号作为该应用程序的唯一标识就相当于数据被你的设备接收经解析需要知道这些数据需要交给那个应用程序处理。例如qq 、微信没见过消息发串了的情况吧。

客户端这边可以不需要手动指定系统会自动随机分配端口号的取值范围是 [0, 65535]。

小于等于1024 的端口号会提供给知名的服务器使用不建议使用这一类。


socket 也是文件用完了要关闭资源。


2.2  DatagramPacket API

DatagramPacket 类是用于基于UDP 协议的Socket 发送和接收的数据报。

只有两个参数的版本不需要设计地址通常用来接收消息。另一个多参数的版本需要显式的设置地址进去通常要用来发送消息。


构造UDP 发送的数据报时需要传入 socketAddress对象这个对象可以使用 InetSocketAddress 来创建 。

InetSocketAddress API 的构造方法


 三、基于UDP Socket 实现客户端服务器程序

分析客户端的程序的功能

1.发出请求消息

2. 等待服务器响应回应

3. 解析服务器的响应

分析服务端的程序的功能
1. 尝试读取客户端的请求并解析

2. 根据客户端的请求统筹响应的数据

3. 把响应返回到客户端


3.1 服务端程序设计

首先我们定义一个服务端的类UdpEchoServer

定义一个 Socket 对象文件与网卡交互。

//需要线定义一个 socket 对象
//通过网络通信必须要使用 socket 对象
private DatagramSocket socket = null;

利用 UdpEchoServer类 的构造方法为 socket  构造并指定需要绑定的端口号

  public UdpEchoServer(int serverPort) throws SocketException {
        //构造 socket 的同时指定要的关联/ 绑定的端口
        socket = new DatagramSocket(serverPort);
  }

绑定一个端口不一定能成功如果某个端口已经被别的进程占用了此时这里的绑定操作就会出错同时一个主机上一个端口同一时刻只能被一个进程绑定。所以这里会抛出异常。


定义一个启动方法start() 

方法内部采用循环的方式每次循环做三件事

1. 读取请求并解析2. 根据请求计算响应3. 把响应的结果写回客服端

public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
            // 每次循环要做三件事情
            // 1. 读取请求并解析
            // 构造接受请求的“空盒子”指定盒子的大小 4096 字节
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            // 数据从网卡上来的
            socket.receive(requestPacket);// 获取客户端的请求、

            //为了方便处理这个请求把二进制数据转换成 String
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根据请求计算响应 调用方法
            String response = process(request);

            /* 3. 把响应结果写回客服端
            根据response 字符串构造一个 DatagramPacket
            和请求 packet 不同此处构造响应的时候需要指定这个包要发给谁 */
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());
            // response.getBytes() 转换为 byte二进制流
            // requestPacket 是从客户端这里收来的getSocketAddress 就会得到客户端的 IP 和 端口
            socket.send(responsePacket);// 响应
            // 可以保存以下日志
            String now3 = df.format(System.currentTimeMillis()); // 返回当前系统时间
            try(Writer writer = new FileWriter("outPut.txt", true)) {
                writer.write(requestPacket.getAddress().toString() +
                        requestPacket.getPort() + " " + now3 +
                        " req: " + request + " resp:  " + response + "\n");
            }
            // 打印一下本次客户端与服务端的交互
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }
 public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();

    }

2. 根据客户端的请求统筹响应的数据我们将第二步计算响应以函数的方式封装实现参数 request 请求我们已经转换为字符串了所以我们就可以依据请求做出对应的处理了如果是需要搜索什么就可以根据请求的内容将服务器的中存储的被搜索内容作为响应返回给客户端如果是互相发消息那就需要根据请求判断需要发给谁此时需要双方约定在请求中添加接收方的IP 和 端口这样才能找到接收方作为服务器端来讲就需要使用多线程的思想每一个客户端发来请求都会分配一个线程来处理。

    public String process(String request) { //这里直接将客户端的请求作为响应返回
        return "服务器响应" + request;
    }

这个部分可以依据客户端的请求进行对应的业务处理。


3.2 客户端程序设计

首先定义一个客户端的类UdpEchoClient 

定义一个 Socket 对象文件与网卡交互。

定义 serverIP 的成员变量用于存储服务器端的IP 地址

定义 serverPort 的成员变量用于存储服务器的端口号

private DatagramSocket socket = null;
private String serverIP = null; // IP
private int serverPort = 0; // 端口号 —— 代表进程的标识

利用 UdpEchoClient 类 的构造方法为 socket  构造并为成员方法关联服务器的 IP 地址和端口

   //客户端启动需要知道服务器在哪儿
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //对于客户端来说不需要显示关联端口
        // 不代表没有端口而是系统自动分配了个空闲的端口
        socket = new DatagramSocket(); //获取随机端口号
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

定义一个启动方法start() 

方法内部采用循环的方式每次循环做4件事

1. 从控制台获取用户输入的信息

2. 将获取的信息构造成 UDP 数据报并向服务器发送请求

3. 客户端尝试获取读取服务器返回的响应

4. 将获取的的响应根据业务需求做进一步的处理

public void start() throws IOException {
        // 通过这个客户端可以多次和服务器进行交互
        System.out.println("客户端启动");
        Scanner in = new Scanner(System.in);
        while (true) {
            //1.先从控制台读取一个字符串过来
            //先打印一个提示符提示用户要输入内容
            System.out.print("->");
            String request = in.next();

            //2. 把字符串构造成 UDP packet, 并进行发送
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(),
                    request.getBytes().length,
                    //----如果参数为null,获得的是本机的IP地址
                    InetAddress.getByName(serverIP),// 确定主机的 IP 地址----在给定主机名的情况下确定主机的IP地址
                    serverPort);
            socket.send(requestPacket); //响应

            //3. 客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);

            //4. 把响应的数据转换成 String 显示出来利用 String 的构造方法转换
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }
 public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start(); // 启动客户端
    }

3.3 总结

以上客户端服务器代码使用了 Socket “ 文件流” 但是没有调用 close() 方法释放资源是因为文件流会随着进程的关闭而关闭但是在实际开发中一定要记得随手关闭哦~

启动客户端和服务器展示效果

 以上操作流程执行图。

 一定是先启动服务器再启动客户端

UDPUser Datagram Protocol是一个无连接的传输层协议它不保证数据包可靠传输。UDP比TCP更快而且在对网络延迟要求较低、对数据准确性要求不高的情况下使用。本文将讨论基于UDP Socket的客户端服务器开发总结。

UDP Socket

Socket是一种用于在计算机之间进行通信的 API应用程序接口。UDP Socket是基于UDP协议进行通信的套接字它提供了一种快速发送和接收数据包的方法但是与TCP不同它不会重复数据或者检查是否有缺失的数据包。

编写UDP客户端的基本步骤如下

  1. 创建一个UDP socket。
  2. 将要发送的数据打包到UDP数据包中。
  3. 使用UDP socket发送数据包到特定的IP地址和端口号。

编写UDP服务器的基本步骤如下

  1. 创建一个UDP socket并监听特定的端口号。
  2. 接收客户端发送的数据包。
  3. 处理数据包然后将响应打包到另一个UDP数据包中。
  4. 使用UDP socket向客户端发送响应数据包。

总结

在使用UDP Socket进行客户端和服务器开发时需要注意以下几点

  • UDP不会保证数据包的可靠传输因此需要在应用程序中处理数据包的丢失或者损坏情况。
  • 由于UDP没有连接状态因此可以同时与多个客户端通信。
  • 在UDP通信中需要指定目标地址和端口号。

好了到这里网络编程中的 基于UDP-Socket 实现客户端服务器通信博主已经分享完了希望对大家有所帮助如有不妥之处欢迎批评指正。 

下期预告TCP 协议

感谢每一个观看本篇文章的朋友更多精彩敬请期待保护小周ღ *★,°*:.☆(▽)/$:*.°★* 

遇见你所有的星星都落在我的头上……

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java服务器