上篇:网络编程基础与UDP套接字编程

上篇:网络编程基础与UDP套接字编程

一、为什么需要网络编程?

平时我们在浏览器里打开优酷看视频,本质上是通过网络获取到了远端的视频资源。和打开本地视频文件类似,区别只在于资源的来源------一个是本地磁盘,一个是网络。

网络资源包括视频、图片、文本等各种数据,而这些资源的传输,都离不开网络编程

二、什么是网络编程?

网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)。

只要满足"不同进程"这个条件,即使是在同一台主机上,基于网络协议传输数据,也属于网络编程。当然,实际开发中,我们最终目的是实现不同主机之间的数据传输。

三、网络编程中的基本概念

1. 发送端和接收端

  • 发送端:数据的发送方进程,对应源主机。
  • 接收端:数据的接收方进程,对应目的主机。
  • 两者是相对概念,一次数据传输中角色可能互换。

2. 请求和响应

获取一个网络资源通常涉及两次传输:

  1. 请求:客户端发送请求数据。
  2. 响应:服务端返回响应数据。

就像点外卖:先下单(请求),商家再送餐(响应)。

3. 客户端和服务端

  • 服务端:提供服务的一方,对外提供服务。
  • 客户端:获取服务的一方。

服务端常见的两种服务模式:

  • 客户端获取服务端资源(如看视频)。
  • 客户端保存资源到服务端(如上传文件)。

4. 常见的客户端服务端模型

  1. 客户端发送请求到服务端。
  2. 服务端处理业务。
  3. 服务端返回响应。
  4. 客户端展示处理结果。

四、Socket套接字

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

按传输层协议分为三类:

  • 流套接字:使用TCP协议,有连接、可靠传输、面向字节流。
  • 数据报套接字:使用UDP协议,无连接、不可靠、面向数据报、大小受限(64K)。
  • 原始套接字:用于自定义传输层协议,简单了解即可。

五、Java数据报套接字(UDP)通信模型

UDP通信基于DatagramSocketDatagramPacket

一次完整的UDP通信流程:

  1. 发送端创建DatagramSocket
  2. 构造DatagramPacket(包含数据、目标IP、端口)。
  3. 调用socket.send(packet)发送。
  4. 接收端创建DatagramSocket并绑定端口。
  5. 调用socket.receive(packet)接收数据。
  6. 解析数据,处理业务,再构造响应包发回。

六、UDP编程核心API

DatagramSocket

方法 说明
DatagramSocket() 绑定任意随机端口(客户端常用)
DatagramSocket(int port) 绑定指定端口(服务端常用)
receive(DatagramPacket p) 接收数据报,阻塞等待
send(DatagramPacket p) 发送数据报,不阻塞
close() 关闭套接字

DatagramPacket

方法 说明
DatagramPacket(byte[] buf, int length) 用于接收数据报
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 用于发送数据报
getAddress() 获取IP地址
getPort() 获取端口号

InetSocketAddress

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

七、UDP Echo Server 代码示例

java 复制代码
public class UdpEchoServer {
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            // 1. 读取请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 计算响应
            String response = process(request);
            // 3. 返回响应
            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(), requestPacket.getPort(), request, response);
        }
    }

    public String process(String request) {
        return request;
    }

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

八、UDP Echo Client 代码示例

java 复制代码
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    public UdpEchoClient(String ip, int port) throws SocketException {
        serverIp = ip;
        serverPort = port;
        socket = new DatagramSocket();
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("客户端启动!!");
        while (true) {
            System.out.print("-> ");
            String request = scanner.next();
            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();
    }
}

九、UDP字典服务器(继承Echo服务器)

java 复制代码
public class UdpDictServer extends UdpEchoServer {
    private Map<String, String> dict = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);
        dict.put("cat", "小猫");
        dict.put("dog", "小狗");
        dict.put("fuck", "卧槽");
    }

    @Override
    public String process(String request) {
        return dict.getOrDefault(request, "该词没有查询到!");
    }

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

十、端口被占用怎么办?

如果启动时出现java.net.BindException: Address already in use,说明端口已被占用。

排查方式:

bash 复制代码
netstat -ano | findstr 端口号

找到PID后,在任务管理器"详细信息"中按PID查找进程,决定关闭或更换端口。

小结

UDP编程简单直接:无连接、面向数据报、一次收发一整块数据。适合对实时性要求高、但允许少量丢包的场景。掌握DatagramSocketDatagramPacket即可完成基本的UDP通信。

相关推荐
泡海椒1 小时前
jquick-pdf 表格行列尺寸控制实战:单元格合并的可行边界
java·开发语言·pdf
Wang's Blog1 小时前
Java 项目实战: 外卖平台-后台系统登录功能开发
java·服务器·redis
海鸥-w1 小时前
spingboot定义一个全局异常处理器
java
xixiaoyunya1 小时前
制造业生产数据备份:在“不停产“前提下的自动化实践
网络·安全
曹牧1 小时前
Jackson 反序列化字段名不匹配
java
名字还没想好☜2 小时前
Spring Boot 优雅停机实战:等在途请求处理完再退出,配合 K8s preStop 别丢请求
java·后端·spring
Figo_Cheung2 小时前
Figo权重动力学:递归本体论与跨尺度演化的形式化基础——揭示:宇宙的演化,本质上就是一场由无数观测者共同参与的、永不停歇的、跨尺度的权重重构盛宴。
网络·人工智能·量子计算
摇滚侠2 小时前
《Spring Boot 3:高级与架构设计》第 1 章 Bean 与 BeanDefinition 个人理解 2
java·spring boot·笔记·后端
qq_269506752 小时前
第30课-请求与响应
java