Java 网络编程:TCP 与 UDP 的「通信江湖」(基于UDP回显服务器)

Java 网络编程:TCP 与 UDP 的「通信江湖」(基于UDP回显服务器)

你有没有想过,当你用 Java 写的聊天工具发送消息,或是用客户端给服务端传数据时,背后藏着两个 "通信高手"------TCP 和 UDP,在 Java 网络编程中,TCP(传输控制协议)与 UDP(用户数据报协议)是传输层的两种核心协议,分别适用于不同的数据交互场景。二者在连接机制、传输可靠性、数据格式等方面存在显著差异,希望这看完这篇博客后,能对其有所了解。

此次所讲的处在应用层,两个协议由操作系统提供的一组api=>socket.api,由于TCP和UDP差别还是比较大的,编写代码的时候,也是不同的风格,因此,socket api 提供了两套不同的api。

一、UDP 协议:无连接的高效传输

UDP的核心特征为:无连接,不可靠传输,面向数据报全双工

1.核心技术特征
  • 无连接:无连接机制,无需建立客户端与服务端的逻辑连接,发送方直接向目标地址发送数据,接收方被动接收数据。也就是对于UDP来说,协议本身,不保存对方的信息。

注意:此连接是抽象概念上的连接,就比如说一对男女朋友去领结婚证,就从男女朋友变成了夫妻,在法律上建立了证明婚姻关系的连接。

  • 不可靠传输:UDP不提供数据传输的可靠性保证。这意味着使用UDP发送数据时,不能保证数据一定会到达目的地,也不能保证数据的顺序和完整性,也就是说UDP就只负责传输,数据丢不丢失,与其无关,只要传了就行。

可不可靠传输就是表明,尽可能的将数据包传输送达,切如果出现丢包,能感知到

  • 面向数据报:面向数据报,读写数据的时候,以一个数据报为单位(不是字符)
  • 全双工:一个通信链路,支持双向通信,能读也能写

全双工看字面确实难以理解,这里以打电话的生活中例子帮助你理解一下:

当你和朋友打电话时,你说出 "今天加班吗?" 的同时,朋友可以直接接话 "不加班,约饭吗?"------ 你发消息(说话)的同时,也能接收朋友的消息(听话);​

不会出现 "你必须等朋友说完,才能开始说" 的限制,双向互动是实时且同时的,这就是全双工。

2. Java 中 UDP 的实现

API 介绍
DatagramSocket:

DatagramSocket是UDP socket,⽤于发送和接收UDP数据报。
构造方法:

⽅法签名 ⽅法说明
DatagramSocket() 创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客⼾端)
DatagramSocket(int port) 创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端⼝(⼀般⽤于服务端)


方法:

⽅法签名 ⽅法说明
void receive(DatagramPacket p) 从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待
void send(DatagramPacket p) 从此套接字发送数据报包(不会阻塞等待,直接发送)
void close() 关闭此数据报套接字


DatagramPacket:

DatagramPacket是UDP Socket发送和接收的数据报,表示一个完整的UDP数据报。
构造方法:

⽅法签名 ⽅法说明
DatagramPacket(byte[] buf, int length) 构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address) 构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号

UDP数据报的载荷数据,就可以通过构造方法来指定

⽅法:

⽅法签名 ⽅法说明
InetAddress getAddress() 从接收的数据报中,获取发送端主机IP址发送的数据报中,获取接收端主机IP地址
int getPort() 从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号
byte[] getData() 获取数据报中的数据
3.构造UDP Echo Server

根据上述api,我们可以构建一个简单的回显服务器

客户端给服务器发一个数据请求,服务器返回一个数据响应,回显服务器就是请求是啥,响应是啥

代码⽰例:
服务器主要思路:

  1. 读取请求并解析
  2. 根据请求。计算响应(服务器最关键的逻辑)
  3. 把响应返回给客户端
  4. 打印一个日志
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);
            DatagramPacket responsePacket=new 
            //3.把响应返回给客户端
            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);


        }
    }

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

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

    }
}

重点细节解析:
回显服务器代码我们思路一下几个步骤来理解:

处理请求的过程,典型的服务器都是分成三个步骤

1.读取数据并请求解析

2.根据请求,计算响应(服务器最关键的逻辑)

此处写的是回显服务器,这个环境就相当于省略了

3...把响应返回给客户端

客户端主要思路:

  1. 从控制台读取用户输入的内容
  2. 把请求发给服务器,需要构造DatagramPacket对象
  3. 发送数据报
  4. 接受服务器的响应
  5. 从服务器读取的数据进行解析,打印出来
java 复制代码
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 requset=scanner.next();
            //2.把请求发给服务器,需要构造DatagramPacket对象
            DatagramPacket requestPacket=new DatagramPacket(requset.getBytes(),requset.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,requestPacket.getLength());
            System.out.println(response);
        }

    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

重点细节解析:


测试代码运行结果


相关推荐
少爷晚安。7 小时前
Java零基础学习完整笔记,基于Intellij IDEA开发工具,笔记持续更新中
java·笔记·学习
悟能不能悟7 小时前
在service方法中已经catch异常,Transactional失效怎么办
java·数据库·sql
西红柿维生素7 小时前
23种设计模式-框架中的使用
java·开发语言·设计模式
LNN20227 小时前
Qt creator +Valgrind检测内存泄漏(linux)
linux·开发语言·qt
日月星辰Ace8 小时前
JDK 工具学习系列(一):javac、java 命令与 main 方法详解
java
球求了8 小时前
Linux 系统入门:环境变量&&虚拟地址空间
linux·运维·服务器·1024程序员节
修炼前端秘籍的小帅8 小时前
精读《JavaScript 高级程序设计 第4版》第6章 集合引用类型(三)Map、WeakMap、Set、WeakSet
开发语言·javascript·ecmascript
@LetsTGBot搜索引擎机器人8 小时前
打造属于你的 Telegram 中文版:汉化方案 + @letstgbot 搜索引擎整合教程
开发语言·python·搜索引擎·机器人·.net
人工智能的苟富贵8 小时前
使用 Tauri + Rust 构建跨平台桌面应用:前端技术的新边界
开发语言·前端·rust·electron