【Socket】之UDP数据报套接字

0. 前言

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

1. 介绍下 API

既然要用到网络编程,就要掌握下。下列API的使用方法

1.1 DatagramSocket API

这是用于创建 socket 对象的,socket 对象相当于对应到系统中一个特殊的文件(socket),socket 文件并非对应到硬盘上的某个数据存储区域,而是直接对应到网卡这个硬件设备!
所以,要想进行网络通信,就需要有socket 文件这样的对象,借助这个 socket 文件对象,才能够间接的操作网卡.
从这个 socket 对象中写数据,相当于通过网卡发送消息
从这个 socket 对象中读数据,相当于通过网卡接收消息

构造方法 方法说明
DatagramSocket() 创建一个UDP数据报套接字的Socket,绑定到本机任意个端口(一般用于客户端)
DatagramSocket(int port) 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)
普通方法 方法说明
void receive(DatagramPacket p) 用来接收数据报,该方法会阻塞等待
void send(DatagramPacket p) 用来发送数据报(不会阻塞,直接发送)
void close 关闭该套接字

1.2 DatagramPacket API

可以理解为创建一个容器,然后将 消息数据放进这个容器中!

构造方法—>接收数据报 方法说明
DatagramPacket(byte[] buf , int length) 构造一个DatagramPacket用来接收数据报,接收的数据放在第一个参数中,第二个参数是接收指定长度
构造方法—>发送数据报 方法说明
DatagramPacket(byte[] buf, int offset , int length , SocketAddress address) 构造一个DatagramPacket用来发送数据报,第一个参数是发送的数据,第个参数是从字符组下标哪里开始,第二个参数是实际的长度,第三个是目的地的IP和端口
DatagramPacket(byte[] buf ,int length, InetAddress address, int port) 构建一个DatagramPacket用来发送数据报,第一个参数是发送的数据,第二个参数是从0到指定的长度,第三个是IP地址,第四个是端口号
普通方法 方法说明
SocketAddress getSocketAddress() 从接收的数据报中,获取发送端主机和IP地址
byte[] getData 获取数据报中的数据
int getPort 从接收的数据包中,获取发送方的端口号;或者从发送方,获取接收方的端口号

2. 代码案例

介绍完上述一些API之后,我们就基于UDP Socket 写一个简单的客户端服务器程序

2.1 服务器端(UdpEchoServer)

一个服务器,主要做三个核心工作

  1. 读取请求并解析
  2. 根据请求计算响应
  3. 把响应返回给客户端
public class UdpEchoServer {
    // 先定义一个 socket 对象
    // 需要网络通信,必须使用socket 对象
    private DatagramSocket socket = null;

    // 绑定一个端口,不一定能成功
    // 如果某个端口已经被别的占用 了,此时这里的绑定操作就会出错
    // 同一个主机上,一个端口,同一时刻,只能被一个进程绑定
    public UdpEchoServer(int port) throws SocketException {
        // 构造 socket 的同时,指定要关联/绑定的端口
        socket = new DatagramSocket(port);

    }

    // 启动服务器的主逻辑
    public void start() throws IOException {
        System.out.println("服务器启动");
        // 每次循环只干3件事
        while (true) {
            // 1.读取请求并解析
            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. 把结果写回客户端
            // 和请求 packet 不同,此处构造响应的时候,需要指定这个包发给谁
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    // requestPacket 是从客户端这里收到的, getSocketAddress 就会获得客户端 ip 和 端口
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    // 此处写的,回显程序;
    // 这是服务器中一个关键环节!
    // 具体的业务
    public String process(String request) {
        return request;
    }

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

2.2 客户端(UdpEchoClient)

客服端做以下事情:
1.获取请求,
2.将请求构造成数据报发送
3.等待服务器解析返回响应
4.把响应展示

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

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

    public void start() throws IOException {
        // 通过这个客户端可以多次和服务器进行交互.
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 先从控制台, 读取一个字符串过来
            //    先打印一个提示符, 提示用户要输入内容
            System.out.print("-> ");
            String request = scanner.next();
            // 2. 把字符串构造成 UDP packet, 并进行发送.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            // 3. 客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            // 4. 把响应数据转换成 String 显示出来.
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            // System.out.println(responsePacket.getSocketAddress());  从服务的返回响应中获取服务器的IP和端口
            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 udpEchoClient = new UdpEchoClient("42.192.83.143", 9090);
        udpEchoClient.start();
    }
}

2.3 流程图

下面用一张图来讲解下,两个代码是如何进行相互配合
【Socket】之UDP数据报套接字_第1张图片
简化后的总结如下
【Socket】之UDP数据报套接字_第2张图片

你可能感兴趣的:(网络编程,udp,网络,tcp/ip)