Java UDP套接字编程:高效实时通信的实战应用与核心类解析

一、网络编程

什么是网络编程?

我们打开视频网站,比如腾讯视频,看仙逆,剑来等动漫,实质上是通过网络,获取到网络上的一个视频资源。

与在本地打开一个视频文件一样,只是这个视频文件来源于网络,相对于本地,网络提供了更为丰富的网络资源。

1.1、网络编程

网络编程是指通过编写程序来实现计算机之间的通信和数据交换。它涉及使用网络协议(如 TCP/IP、UDP等)在网络上发送和接收数据。(网络上的主机,通过不同的进程,以编码的方式实现网络通信)

即便是同一个主机,只要进程不同,基于网络来传输数据,也属于网络编程。

1.2、网络编程的核心概念

1.2.1、客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端。

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

1.2.2、Socket套接字

概念:Socket套接字是由系统提供用于网络通信的基本操作单元

基于Socket套接字的网络程序开发就是网络编程。

主要分为以下三类:

(1)流套接字

使用传输层TCP协议

TCP(Transmission Control Protocol)传输控制协议

特点:

• 有连接

• 可靠传输

• 面向字节流

• 有接受缓存区,也有发送缓存区

• 大小不限

字节流:基于IO流以字节为单位进行数据传输,数据在流未关闭的情况下可以持续发送和接受.

(2)数据报套接字

使用传输层UDP协议

UDP(User Datagram Protocol)用户数据报协议

特点:

• 无连接

• 不可靠传输

• 面向数据报

• 有接受缓存区,无发送缓存区

• 大小受限:一次最多传输64K

数据报:传输是原子的,发送和接受操作必须一次性完成,发送方需将数据封装成一个数据报,接收方则接受整个数据报。

(3)原始套接字

**原始套接字(Raw Socket)**是一种允许应用程序直接访问网络层协议的套接字类型。与常见的流式套接字(如TCP)或数据报套接字(如UDP)不同,原始套接字允许用户自定义网络层协议头,甚至可以直接发送和接收原始数据包。(此知识我们不过多讲解)

1.3、数据报套接字通信模型

上文UDP,即用户数据报协议是无连接,面向数据报的特征,每次都是没有建立连接,并且一次性发送所有数据报,一次性接受所有数据报。

Java中使用UDP协议通信,主要是基于DatagramPacket 类来创建数据报套接字 ,并使用DatagramPacket作为发送或接受的UDP数据报,对于一次发送及接受UDP数据报的流程如下:

1.4、流套接字通信模型

TCP,即传输控制协议是有连接的,基于IO流,持续发送和接收的协议。

Java使用TCP协议通信,在服务端创建ServerSocket并绑定端口创建Socket连接ServerSocket对象接受客户端的Socket对象调用方法实现通信 ;在客户端创建Socket对象指定服务端地址和端口。

不论是客户端和服务端都要在通信完成后关闭流对象,服务端要多关闭一个Socket对象,客户端的Socket对象会随通信完成后进程的结束而自动关闭~~

流程如下 :

1.5、 Socket编程注意事项

(1)客户端和服务端:开发时,经常是基于一个主机开启两个进程作为客户端和服务端,但在真实情况下。一般都是不同主机。

(2)目的IP和端口号,标识了一次数据传输时要发送数据的终点主机和进程。

(3)Socekt编程是我们使用流套接字和数据报套接字,基于传输层的TCP或UDP协议,但在应用层协议,也需要考虑(后续更新)。

(4)端口占用,如果一个进程Q已经绑定了一个端口,在启动一个进程P绑定该端口,就会报错,报错信息如下:

我们可以在 cmd 中输入 netstat -ano | findstr 端口号,可以显示对应进程的 pid

然后在任务管理器 中,通过 pid查看进程 :

解决端口被占用的问题:

(1)如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B.

(2)如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

二、 UDP数据报套接字编程

2.1、API介绍

DatagramSocket(通信端点)

DatagramSocket是UDP Socket,用于发送和接收UDP数据报。

构造方法

DatagramSocket 方法

DatagramPacket(数据容器)

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

构造方法

DatagramPacket方法

InetSocketAaddress

InetSocketAaddress是(SocketAddress的子类)构造方法

2.2、模拟实现服务器

2.2.1、完整代码
java 复制代码
package UdpEcho;

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

public class UdpEchoServer {
    private DatagramSocket datagramSocket=null;
    public UdpEchoServer(int port) throws SocketException {
        datagramSocket=new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            DatagramPacket requestPacket=new DatagramPacket(new byte[4048],4048);
            datagramSocket.receive(requestPacket);
            String request=new String(requestPacket.getData(),0,requestPacket.getLength());
            String response=procsee(request);
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            datagramSocket.send(responsePacket);
            System.out.printf("[%s:%d]   req:%s    rep:%s/n",
                    requestPacket.getAddress(),requestPacket.getPort(),request,response);
        }
    }
    public String procsee(String s){
        return s;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server=new UdpEchoServer(8776);
        server.start();
    }
}
2.2.2、核心组件
2.2.3、服务启动流程
2.2.4、与客户端交互流程图

2.3、模拟实现客户端

2.2.1、完整代码
java 复制代码
package UdpEcho;

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

public class UdpEchoClient {
    private String IP;
    private  int Port;
    private DatagramSocket socket=null;
    public UdpEchoClient(String ip,int port) throws SocketException {
        this.IP=ip;
        this.Port=port;
        socket=new DatagramSocket();
    }
    public void start() throws IOException {
        Scanner sc=new Scanner(System.in);
        while(true) {
            if (!sc.hasNext()){
                break;
            }
            System.out.println("请输入您要发送的内容:");
            String request=sc.next();
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(IP),Port);
            socket.send(requestPacket);
            DatagramPacket responsePacket=new DatagramPacket(new byte[4048],4048);
            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",8776);
        client.start();
    }
}
2.2.2、核心组件

UDP是无连接的,故要定义IP和端口号,构造方法中Socket未指定本地端口号,由系统自动分配以避免冲突。

java 复制代码
    private String IP;
    private  int Port;
    private DatagramSocket socket=null;
    public UdpEchoClient(String ip,int port) throws SocketException {
        this.IP=ip;
        this.POrt=port;
        socket=new DatagramSocket();
    }
2.2.3、通信流程
java 复制代码
   public void start() throws IOException {
        Scanner sc=new Scanner(System.in);
        while(true) {
            if (!sc.hasNext()){
                break;
            }
            System.out.println("请输入您要发送的内容:");
            String request=sc.next();
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(IP),POrt);
            socket.send(requestPacket);
            DatagramPacket responsePacket=new DatagramPacket(new byte[4048],4048);
            socket.receive(responsePacket);
            String response=new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }
2.2.4、与服务端交互流程
相关推荐
bing_15824 分钟前
Spring Data MongoDB 提供了哪些核心组件?
java·mongodb·spring
知秋丶30 分钟前
Spring-rabbit重试消费源码分析
java·后端·spring
hello早上好33 分钟前
Spring Bean后处理器
java·架构
沉豆41 分钟前
Jmeter调用jar包中的方法,并使用返回值当请求参数
java·jmeter·jar
天天摸鱼的java工程师1 小时前
synchronized 与 ReentrantLock 区别?公平锁、非公平锁、可重入锁、自旋锁的原理与应用?
java·后端
一只白鸭子1 小时前
从实践到思考:Spring Boot + MyBatis关系查询小分享
java
天天摸鱼的java工程师1 小时前
详解 Spring Boot 的 RedisAutoConfiguration 配置:从自动装配到自定义扩展
java·后端
Dcs1 小时前
Java 新手指南:类和对象到底是啥?
java
用户正在当牛马1 小时前
Mysql的数据如何与redis进行同步(双写一致性)
java
程序员阿超的博客1 小时前
Java大模型开发入门 (7/15):让AI拥有记忆 - 使用LangChain4j实现多轮对话
java·人工智能·microsoft