网络编程UDP

操作系统会提供一组API(socket.api)接口进行网络通讯

传输层的两个核心协议

TCP:有连接,可靠传输,面向字节流,全双工

UDP:无连接,不可靠传输,面向数据报,全双工

有连接vs无连接

这个连接不是物流意义上的连接,是抽象,虚拟概念的连接。

在TCP中,A和B通信,A和B先建立连接,然后A会保存B的信息,B也会保存A的信息,让他们知道彼此之间是和谁在进行通信。

UDP中,则不会保存这样的信息,不管对方是谁,这个就叫做无连接

举例子

结婚,两个人结婚就之后,会有结婚证,一式两份,上面就会写着对方的信息,比如A是B的老公,B是A的老婆。这个就叫做有连接

可靠传输vs不可靠传输

在网络传输中,丢包是很常见的事情,受到外界的各种干扰就会导致丢包,当我们发出一个数据包的时候,并不指望他能够100%到达对方

可靠传输:并不能100%的送达,只是说尽可能的送达

不可靠传输:管你那么多,我送了,到不到就不关我事了

可靠传输虽然送达的完整度高,但是要付出效率低的代价,不可靠传输虽然送达完整度低,但是效率高。

面向字节流vs面向数据报

面向字节流:以字节为基本单位读写数据,支持任意长度会出现粘包的问题(读一半)

面向数据报:以一个数据报为基本单位读写数据,每次读取必须1个完整的数据报,不支持任意长度但是不会出现粘包

全双工vs半双工

全双工:一个通信链路中,支持双向通信,能读也能写

半双工:一个通信链路中,支持单向通信,只能写或者只能读

Socket.api进行网络编程

在计算机中,文件广义上来说,是操作系统操作管理的一种形式,同时,硬件也可以抽象成一个文件,进行统一的管理。

网卡==socket文件

操作过程就和操作普通的文件差不多,打开,读写,关闭,之所以不直接操作网卡,是因为不好操作,所以转化成文件,相当于遥控器

DategramSocket

表示的是通过操作系统调用socket.api来操作网卡

构造方法:相当于打开文件

第一个构造方法是随机绑定一个端口号

第二个是指定端口号

一个是接收,一个是发送,注意传入的是一个完整的数据报,因为你接收和发送的肯定是一个完整的数据报

DatagramPacket

表示的是一个完整的UDP数据报

UDP的数据包的载荷数据,可以通过构造方法来指定传入和传出

使用UdpEchoClient/Server模拟(回显服务器)

UdpEchoServer

思路

首先声明一个变量,类型是datagramsocket,然后在构造方法中传入一个端口号,同时初始化之前的变量,这个变量是datagramsocket类型的,这个类就相当于一个代理人,因为我们无法直接调用网卡,这个类就相当于一个代理人向操作系统申请一个窗口和网卡交流,,之后在start方法中要写入一个死循环,因为我们要让服务器进行不间断的工作,服务器的处理请求的过程一般分为三步,第一步,创建一个UDP数据包,读取请求,最好使用一个数组类型这样读取的快一些,然后使用socket.receive去读取,注意这个是一个阻塞的方法,会等待客户端输入,假如客户端输入了,因为网络传输的只能说二进制,所以下一步就是需要把这些二进制转成string类型,第二步就是响应请求,这里我用的是回显式,所以暂时先不管,第三步就是返回数据给客户端,我们要把之前转成字符串的转成二进制,这样才能在网络上传播,传播的时候要注意指定ip和端口号,最后返回即可。

具体代码实现:

这段代码中,首先是声明一个datagramsocket类型的变量,然后在构造方法中,我会传入一个端口号,因为我们在java中是无法直接操作网卡的,所以要借助datagramsocket这个类,可以理解为代理人,去和操作系统申请,让我们可以和网卡进行沟通,进而可以进行读写操作。

这里更是核心中的核心,首先,因为服务器需要不间断的进行工作,所以在这个start方法中,我们需要加入一个死循环来确保他能够24小时不间断工作(黑奴)。

服务器处理请求一般是分为三步

第一步,读取并解析请求

首先,我们需要提前准备好一个空的请求包,这个是为了之后在客户端输入的时候,我们能够读取到信息,然后使用socket.receive去读取这个数据包,接着需要把这些去读到的数据转化成字符串,方便我们能看懂

第二步,根据请求,计算响应(核心)

由于我这里演示的是回显式所以就比较简单

第三步,返回响应值

因为之前我们把他拆成字符串了,所以这个时候我们需要把他变回去成为二进制的数据,然后把他装到一个数据包里去,这个时候要注意给出发送的地址和端口号,最后发送即可。

可能你会有一个疑问,之前不是说UDP是无连接的吗,这个指的是长久的连接,一次的连接他是可以记住的,过了就忘了,所以这个时候可以获取到源IP和端口。

可能你还有一个疑问,既然说网卡可以抽象成文件,那打开不要关闭吗?好问题,不需要关闭,因为我们这个是服务器,关了还咋服务。

还有一个疑问,当客户端没有发消息的时候,这个服务器在干啥?忙等吗?并没有,还记得我之前说receive方法那里吗,带有阻塞的兄弟,会阻塞等待直到客户端有消息传入。

最后一步,打印日志

前面的是格式,后面的是一一对应的东西,分别打印请求的ip和端口号,还有请求是啥,回应是啥。

源码

java 复制代码
package NetWork;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhany
 * Date: 2025-12-14
 * Time: 13:04
 */
public class UdpEchoServer {
    //socket是一个可以和网卡通讯的,读就相当于在网卡上收取信息,写就是通过网卡发送
    //首先,先定义一个变量用来存放之后的行为
    DatagramSocket socket = null;

    public UdpEchoServer (int port) throws SocketException {
//指定一个端口号,让服务器使用
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        //因为服务器需要7*24小时不间断工作,所以套一个while死循环
        while (true){
            //处理请求的过程分为三步
            //1.读取请求并解析
            //这一步是创建一个UDP的数据包,传入的字节数组,就是UDP的载荷
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(requestPacket);
            //把读取到的二进制转成有效的字符串,使用getdata获取到有效的字节数组,getlength获取到有效的长度,再string
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.根据请求,计算响应
            //因为写的是回显式服务器,所以不用管先
            String response = process(request);
            //3.返回响应值
            //因为前面把他转成了字符串,所以现在需要把他转成二进制的要注意最后不是response.length,同时要给出这个
            //回应的端口号和ip
    DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
            requestPacket.getSocketAddress());
            socket.send(responsePacket);
//打印日志
            System.out.printf("[%s : %d] , req: %s , resp: %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 udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}
 

UdpEchoClient

思路

客户端其实和服务器端有点类似,他也没有办法直接操作网卡,所以他也需要寻找一个代理人datagramsocket去帮他开个通话窗口去读写网卡的数据,逻辑也是比较简单的,首先我们知道UDP是无连接的,所以我们需要在构造方法中传入一个源地址和端口号,让我们知道去访问哪个服务器,核心,让用户输入要发送的内容,然后我们需要构建一个数据包载荷,里面把用户输入的全部转成二进制的,需要知道这个有啥和有多长,其次需要知道这个包要去到哪里,哪个端口号,然后发送出去就ok,然后需要创建一个新的数据包(载荷),等待服务器返回的值,然后把返回的二进制解析成为字符串,然后展示出来,结束

第一步

首先创建一个datagramsocket类型的变量,因为这个和服务端的一样,是无法直接操作网卡的,所以也是需要借助这个代理人的,因为UDP是无连接,所以他不知道要去到哪里,这个时候我们就需要告诉他去到哪里,在构造方法中,需要传入访问的IP地址和端口号。

核心方法start

首先让用户输入想要发送的,然后接下里就简单了,把用户输入的直接开始分解,分解成二进制的数据,放到一个数据包中去,注意在创建这个数据包的时候,需要指定传输的ip和端口号,然后发送就行,接着需要创建好一个空的数据包,用来读取服务器返回的值,再把返回来的值解析成字符串,最后打印出来

注意这个"127.0.0.1"是环回IP,无论你本机的真实IP是啥,都是可以进行访问的

这两个程序在同一个电脑上确实是可以进行互传消息的,那如果在两个电脑上呢?

  1. 如果是在同一个局域网的情况下,是可以进行互发消息的
  2. 如果不在同一个局域网下就无法进行发消息

源码

java 复制代码
package NetWork;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: zhany
 * Date: 2025-12-14
 * Time: 18:52
 */
public class UdpEchoClient {
    private DatagramSocket socket = null;
    //因为UDP并不会记录对方是谁,所以要自己保存
    private String serverIp;
    private int serverPort;

    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
        socket = new DatagramSocket();
        //这里必须用无参的,因为客户端如果是固定的不太可能,可以是固定但是非常不可能
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("请输入你要发送的内容");
            if (!scanner.hasNext()){
                break;
            }
            String request = scanner.nextLine();
            //构造数据载荷的同时要注意ip和端口号

            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            //发送
            socket.send(requestPacket);
            //接收
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            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 udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start();
    }

}
 
相关推荐
大猫子的技术日记2 小时前
【工具篇】极简入门 UV Python项目管理工具
开发语言·python·uv
古城小栈2 小时前
边缘计算:K3s 轻量级 K8s 部署实践
java·kubernetes·边缘计算
武子康2 小时前
Java-196 消息队列选型:RabbitMQ vs RocketMQ vs Kafka
java·分布式·kafka·rabbitmq·rocketmq·java-rocketmq·java-rabbitmq
小年糕是糕手2 小时前
【C++同步练习】类和对象(三)
开发语言·jvm·c++·程序人生·考研·算法·改行学it
CS创新实验室2 小时前
计算机考研408【计算机网络】核心知识点总结
网络·计算机网络·考研·408
m0_740043732 小时前
SpringBoot02-SpringMVC入门
java·开发语言·spring boot·spring·mvc
wadesir2 小时前
Judy数组:C语言中的高性能动态数组(全面入门Judy库使用指南)
c语言·开发语言
csbysj20202 小时前
SQLite Glob 子句详解
开发语言
Seven972 小时前
字符串匹配算法
java