Java【网络原理】(2)初识网络续与网络编程

发布于:2025-02-25 ⋅ 阅读:(13) ⋅ 点赞:(0)


目录

1.前言

2.正文

2.1TCP协议与UDP协议

2.2socket API进行网络编程

2.2.1DatagramPacket类

2.2.1.1发送数据报

2.2.1.2接收数据报

2.2.1.3获取数据报内容

2.2.1.4设置数据报内容

2.2.2DatagramSocket类

2.2.2.1构造方法

2.2.2.2常用方法

2.2.3具体代码与解释

3.小结


1.前言

哈喽大家好吖,今天继续给大家分享计算机网络相关的知识,先讲解传输层两个重要的UDP和TCP协议,再讲解在UDP中通过socket API来进行网络编程。

2.正文

2.1TCP协议与UDP协议

在讲解二者的概念与对比之前,先提前介绍几个涉及到的概念:

有连接/无连接:指的是通信的A和B两端是否互相保留对方的信息(即彼此之间是否知道是谁和它建立连接)。

可靠传输/不可靠传输:在网络上,很容易出现丢包这种情况,因为光信号和电信号都可能受到外界干扰,如果在传输中识别到某些出错的数据,打包丢掉,保证正常数据传输,这个叫做可靠传输,反之只管发送,不管中间的错误就是不可靠传输。

面向字节流/面向数据包:面向字节流,读写数据的时候,是以字节为单位面向数据报,读写数据的时候,以一个数据报为单位(不是字符)。

全双工/半双工:一个通信链路,支持双向通信(能读,也能写) / 一个通信链路,只支持单向通信~~(要么读,要么写)

讲解完以上概念,正文就可以顺利开始~ 

TCP(传输控制协议)和UDP(用户数据报协议)是互联网中最重要的两种传输层协议,负责在应用程序之间传输数据。它们在设计目标、工作原理和应用场景上有显著差异。以下是详细的对比和解析:

TCP

特点:

  • 面向连接:通信前需通过三次握手建立可靠连接,结束时通过四次挥手释放连接。

  • 可靠传输:通过确认应答(ACK)、超时重传、数据排序、流量控制和拥塞控制确保数据完整、有序、无丢失。

  • 全双工通信:支持双向数据流传输。

  • 字节流模式:数据被看作无结构的字节流,由TCP自行分割和重组。


优点:

  • 数据可靠性高,适合文件传输、网页浏览等场景。

  • 自动处理数据分片、重组和错误恢复。

缺点:

  • 建立和释放连接的开销大。

  • 传输延迟较高。


UDP

特点:

  • 无连接:直接发送数据,无需建立连接。

  • 不可靠传输:不保证数据到达、顺序或完整性。

  • 数据报模式:每个数据包独立处理,保留边界。

  • 全双工通信:支持双向数据流传输。


优点:

  • 传输速度快、延迟低。

  • 资源占用少,适合实时性要求高的场景。

  • 支持广播和多播(一对多通信)。

缺点:

  • 不保证数据可靠性,可能丢失或乱序。

2.2socket API进行网络编程

接下来,就到了咱们的代码环节了,我们将利用socket API来创建一个本地的UDP回显服务器。那么何为回显服务器呢?

UDP回显服务器是一种简单的网络服务,它接收客户端发送的UDP数据报,并将相同的数据报原样返回给客户端,即请求和响应相同。

2.2.1DatagramPacket类

在开始创建一个本地的UDP回显服务器之前,需要先介绍一下我们要使用的DatagramPacket类:

DatagramPacket类用于封装UDP数据报。它表示一个UDP数据报的结构,包括数据内容、目标地址、目标端口、数据长度等信息。

2.2.1.1发送数据报
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
  • buf:要发送的数据,存储在字节数组中。

  • length:要发送的数据的长度。

  • address:目标主机的 IP 地址,类型为 InetAddress

  • port:目标主机的端口号。


2.2.1.2接收数据报
DatagramPacket(byte[] buf, int length)
  • buf:用于存储接收到的数据的字节数组。

  • length:字节数组的长度,即最大可接收的数据长度。


2.2.1.3获取数据报内容
  • getData():返回数据报的数据,类型为 byte[]

  • getLength():返回数据报的实际数据长度。

  • getAddress():返回发送方的 IP 地址(接收时使用)。

  • getPort():返回发送方的端口号(接收时使用)。


2.2.1.4设置数据报内容
  • setData(byte[] buf):设置数据报的数据。

  • setLength(int length):设置数据报的长度。

  • setAddress(InetAddress address):设置目标地址。

  • setPort(int port):设置目标端口。

2.2.2DatagramSocket类

DatagramSocket类用于发送和接收UDP数据报。它表示一个UDP端点,可以绑定到本地端口,并通过网络发送和接收数据报。

2.2.2.1构造方法
  • DatagramSocket():系统随机分配一个未使用的本地端口。
  • DatagramSocket(int port):绑定指定的本地端口。
  • DatagramSocket(int port, InetAddress address):绑定到指定的本地端口和地址。

2.2.2.2常用方法
  • void send(DatagramPacket p):发送数据报。
  • void receive(DatagramPacket p):接收数据报。
  • void close():关闭套接字。

2.2.3具体代码与解释

下面是具有详细的注释的代码与讲解:

UdpEchoServer:

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

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 表示一个 UDP 数据报. 此处传入的字节数组, 就保存 UDP 的载荷部分.
            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. 把响应返回给客户端
            //    根据 response 构造 DatagramPacket, 发送给客户端.
            //    此处不能使用 response.length()
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            //    此处还不能直接发送. UDP 协议自身没有保存对方的信息(不知道发给谁)
            //    需要指定 目的 ip 和 目的端口.
            socket.send(responsePacket);

            // 4. 打印一个日志
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(), requestPacket.getPort(),
                    request, response);
        }
    }

    // 后续如果要写别的服务器, 只修改这个地方就好了.
    // 不要忘记, private 方法不能被重写. 需要改成 public
    public String process(String request) {
        return request;
    }

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

 UdpEchoClient:

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

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) {
            // 1. 从控制台读取用户输入的内容.
            System.out.println("请输入要发送的内容:");
            if (!scanner.hasNext()) {
                break;
            }
            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 {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

 

实现核心思路:

  1. 服务器端

    • 打开一个UDP套接字(Socket)。

    • 绑定到一个指定的端口,等待客户端发送数据。

    • 接收客户端发送的UDP数据报。

    • 将接收到的数据报原样发送回客户端。

    • 重复上述过程,直到服务器关闭。

  2. 客户端

    • 打开一个UDP套接字。

    • 向服务器的指定端口发送数据报。

    • 接收服务器返回的数据报。

    • 比较发送和接收的数据是否一致,以验证数据传输的完整性。

    • 关闭套接字。

3.小结

今天的分享到这里就结束了,喜欢的小伙伴不要忘记点点赞点个关注,你的鼓励就是对我最大的支持,加油!