JAVAEE之网络编程(1)_套接字、UDP数据报套接字编程及从代码实例

时间:2024-06-09 07:14:40

前言

什么是网络编程呢?
 网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。当然,即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程

一、网路编程中的基本概念

1.1 发送端和接收端

  1. 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
  2. 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
  3. 收发端:发送端和接收端两端,也简称为收发端。

注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。

请求和响应

一般来说,获取一个网络资源,涉及到两次网络数据传输:

  1. 第一次:请求数据的发送
  2. 第二次:响应数据的发送。

客户端和服务端

客户端和服务端:

  1. 服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
  2. 客户端:获取服务的一方进程,称为客户端。

二、Socket 套接字

2.1 基本概念

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

2.2数据报套接字:使用传输层UDP协议

UDP,即User Datagram Protocol(用户数据报协议),传输层协议。
UDP的主要特点:(详细内容会在后续的章节中详细介绍)

  1. 无连接
  2. 不可靠传输
  3. 面向数据报
  4. 有接收缓冲区,无发送缓冲区
  5. 大小受限:一次最多传输 64KB

 对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

三、UDP数据报套接字编程

3.1 Java数据报套接字通信模型

 对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报。
 java中使用UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使用DatagramPacket 作为发送或接收的UDP数据报

对于一次发送及接收UDP数据报的流程如下:
在这里插入图片描述

3.2 DatagramSocket API

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 构造方法:
在这里插入图片描述
DatagramSocket 方法:
在这里插入图片描述

3.3 DatagramPacket API

DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:
在这里插入图片描述
 构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
InetSocketAddress ( SocketAddress 的子类 )构造方法:
在这里插入图片描述

DatagramPacket 方法:
在这里插入图片描述

以下为服务端和客户端代码:

UDP服务端:

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.channels.DatagramChannel;

/**
 * @author Zhang
 * @date 2024/5/2015:38
 * @Description:
 */
public class UdpEchoServer {

    //创建一个datagramSocket对象,这是后续操作网卡的基础
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        //此写法是手动指定端口
        socket = new DatagramSocket(port);

        // 此写法是让服务器自动分配端口
       // socket = new DatagramSocket();

    }

    //通过这个方法来启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");

        while(true){
            // 1. 读取请求并解析
            /**
             *  DatagramPacket这个队形来承载从网卡这边读取的数据,
             *  收到的数据需要一个内存空间来保存这个数据
             *  DatagramPacket 内部不能自动分配空间,需要程序员手动创建,交给DatagramPacket进行处理
             */
            DatagramPacket requestPacket  = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);   //当服务器一旦启动,就会立即执行receive方法,入伏哦客户端请求还没到,receive就会阻塞到客户端请求到达
            // 当完成receive后,数据以二进制的形式存储到DatagramPacket
            // 要想把这里的数据显示出来,好需要把这个二进制转变成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

            // 2.根据请求计算响应(一般服务器都会经理的过程)
            // 由于此处是回显服务器,请求是啥样,响应就是啥样
            String response = process(request);

            // 3. 把响应写会到客户端
            //      写一个响应对象,DatagramPacket
            //      往DatagramPacket 构造刚才数据,并通过 sent 返回

            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
            // 构造这个数据报,需要指定数据内容,也指定下一个数据报要发给谁
            socket.send(responsePacket);

            // 4.打印一个日志,把这次数据交互的详情打印出来
            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 server = new UdpEchoServer(9090);
        server.start();
    }
}

UDP客户端

package network;

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

/**
 * @author ZhangXu
 * @date 2024/5/2015:38
 * @Description:
 */
public class UdpEchoClient {

    private DatagramSocket socket = null;
    private  String serverIP = "";
    private  int serverPort = 0;

    public  UdpEchoClient(String ip,int port) throws SocketException {
        // 创建这个对象,不能手动指定端口
      socket = new DatagramSocket();
      //由于UDP自身不会持有对端的信息,就需要在应用程序里,吧对端的情况给记录下来
        serverIP = ip;
        serverPort = port;
    }


    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true){
            // 1.从控制台读取数据,作为请求
            System.out.print("->");
            String request = scanner.next();
            // 2. 把请求内容构造成 DatagramPacket 对象,发送给服务器
            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 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();
    }

}


 启动客户端和服务端,在客户端发送内容,在服务端将会接收到相同的信息,运行结果:

UdpEchoClient:   
	客户端启动!
	->xiaoming
	xiaoming
->

UdpEchoClient:   
	服务器启动
	[/127.0.0.1:51575] req = xiaoming, resp = xiaoming

我们在下一节内容将会介绍TCP流套接字编程相关内容。