探索Java网络编程精髓:UDP与TCP的实战魔法!

news2024/11/15 12:58:31

Java 中提供了专门的网络编程程序包 java.net,提供了两种通信协议:UDP(数据报协议)和 TCP(传输控制协议),本文对两种通信协议的开发进行详细介绍。

1

UDP 介绍

d665f616a4c6db554d837097c15c5958.png

UDP:User Datagram Protocol,是一种无连接的传输层协议,是不可靠的消息传输服务。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

UDP 的特点是非面向连接,传输不可靠,数据可能丢失。一般用于各种聊天工具、音频或者多媒体应用,对数据传输的完整性要求不是特别高的场景。

在 UDP 开发中使用 DatagramPacket 类包装一条要发送的消息,然后用 DatagramSocket 类完成消息的发送。

下面我们来通过一个的示例来了解 UDP 编程,编写一个客户端程序,在客户端里指定要接收数据(服务端)的端口以及要发送的数据,打包数据后进行发送;编写一个服务端程序,在服务端里接收客户端发来的数据并输出。

// 客户端发送消息
@Test
  public void testClient() throws IOException {
    // 1、创建客户端指定端口为6666
    DatagramSocket client = new DatagramSocket(6666);
    // 2、准备数据
    String msg = "测试UDP!";
    byte[] data = msg.getBytes();
    // 3、打包数据,InetSocketAddress端口为服务端的端口
    DatagramPacket packet = new DatagramPacket(data, data.length, new InetSocketAddress("localhost", 8888));
    // 4、发送数据
    client.send(packet);
    // 5、释放资源
    client.close();
  }


// 服务端接收消息
@Test
  public void testServer() throws IOException {
    // 1、创建服务端指定端口为8888
    DatagramSocket server = new DatagramSocket(8888);
    // 2、准备接受输入的缓存字节数组
    byte[] buf = new byte[1024];
    // 3、封装成DatagramPacket包
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    // 4、接受数据
    server.receive(packet);
    // 5、分析数据
    byte[] data = packet.getData();
    System.out.println(new String(data, 0, packet.getLength()));
    // 6、释放资源
    server.close();
  }

首先运行服务端程序,然后运行客户端程序,观察服务端程序的控制台的运行结果:

测试UDP!

下面我们结合 IO 流,来演示一个发送基本数据类型的示例,客户端向服务端发送一个用户的名称和年龄,假设用户名为 JPM,年龄为 18 岁。

// 客户端发送数据
@Test
  public void client() throws IOException {
    // 1、创建客户端指定端口为6666
    DatagramSocket client = new DatagramSocket(6666);
    // 2、准备数据
    String name = "JPM";
    int age = 18;
    byte[] data = buildData(name, age);
    // 3、打包数据,InetSocketAddress端口为服务端的端口
    DatagramPacket packet = new DatagramPacket(data, data.length, new InetSocketAddress("localhost", 8888));
    // 4、发送数据
    client.send(packet);
    // 5、释放资源
    client.close();
  }


  private byte[] buildData(String name, int age) throws IOException {
    byte[] data = null;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);
    dos.writeUTF(name);
    dos.writeInt(age);
    dos.flush();
    data = bos.toByteArray(); // 获取数据
    dos.close();
    bos.close();
    return data;
  }


// 服务端接收数据
@Test
  public void server() throws IOException {
    // 1、创建服务端指定端口为8888
    DatagramSocket server = new DatagramSocket(8888);
    // 2、准备接受输入的缓存字节数组
    byte[] buf = new byte[1024];
    // 3、封装成DatagramPacket包
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    // 4、接受数据
    server.receive(packet);
    // 5、分析数据
    String data = getData(packet.getData());
    System.out.println(data);
    // 6、释放资源
    server.close();
  }


  private String getData(byte[] data) throws IOException {
    DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
    String name = dis.readUTF();
    int age = dis.readInt();
    dis.close();
    return name + "的年龄是:" + age;
  }

首先运行服务端程序,然后运行客户端程序,观察服务端程序的控制台的运行结果:

JPM的年龄是:18

2

TCP 介绍

ed942bf4169d316ba5ea79d27c506114.png

TCP,Transmission Control Protocol,是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 的特点是面向连接的、点到点的、高可靠的,需要通过三次握手建立连接。

在 Java 中使用 Socket(套接字)完成 TCP 程序的开发。服务器端使用 ServerSocket 类来接受客户端的连接,每一个客户端都使用一个 Socket 对象表示。

服务器端要使用 accept() 方法等待客户端的连接,此方法执行后服务器端进入阻塞状态,直到客户端连接之后程序才可以继续执行。accept() 方法返回的是一个 Socket 类型的对象,每一个 Socket 对象都表示一个客户端对象。

在客户端,可以通过 Socket 类的 getInputStream() 方法获取服务器的输出信息,在服务器端可以通过 Socket 类的 getOutputStream() 方法获取客户端的输出信息,因此说网络程序员要使用 Java IO 输入、输出流来完成信息的传递。

下面演示一个 TCP 程序的示例,服务器接收到客户端的连接后返回给客户端“Hello,欢迎使用!”,客户端接收服务端的数据并输出到控制台。

// 服务器端程序
  @Test
  public void testServer() throws IOException {
    // 1、创建服务器指定8888端口
    ServerSocket server = new ServerSocket(8888);
    // 2、接收客户端连接,阻塞式
    Socket socket = server.accept();
    System.out.println("建立客户端连接");
    // 3、发送数据
    String msg = "Hello,欢迎使用!";
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    bw.write(msg);
    // 必须调用此方法,否则客户端readLine()会执行会发生Connection reset异常
    bw.newLine();
    bw.flush();
    server.close();
  }
// 客户端程序
  @Test
  public void testClient() throws IOException {
    // 1、创建客户端连接,指定服务器的域名和端口
    Socket client = new Socket("localhost", 8888);
    // 2、接收数据
    BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
    String msg = br.readLine(); // 阻塞式方法
    System.out.println(msg);
    client.close();
  }

首先运行服务端程序,然后运行客户端程序,观察控制台的运行结果:

服务端控制台输出:
建立客户端连接


客户端控制台输出:
Hello,欢迎使用!

注意:如果服务端已经开启一个 8888 端口的服务,那么再次开启 8888 端口服务端的程序会发生绑定端口异常,如:java.net.BindException: Address already in use: JVM_Bind

以上的程序一个服务端只能接收一个客户端,并只能处理一次客户端请求,如果想实现一个服务端可以接收多个客户端的连接,那么需要在服务端程序上加入一个 while 无限循环来完成。

3

TCP 通信的经典案例

0025806a6d9203cff2dc800c02d74911.png

下面的代码实现网络编程通信的一个经典案例,就是客户端输入消息,服务器接收到客户端的消息后,在客户端发来的消息内容前面加上“echo:”并返回给客户端,直到客户端输入“exit”时退出连接。

// 服务器端程序
@Test
  public void testMultiServer() throws IOException {
    // 创建服务器指定8888端口
    ServerSocket server = new ServerSocket(8888);
    Socket client = null;
    PrintStream out = null;
    BufferedReader buf = null;
    boolean sts = true;// 默认无限循环
    // while循环接收客户端连接
    while (sts) {
      System.out.println("======服务器已经运行,等待客户端的链接:======");
      client = server.accept();
      System.out.println("建立客户端连接");
      // 得到客户端的输入信息
      buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
      // 实例化客户端的输出流,用于输出消息
      out = new PrintStream(client.getOutputStream());
      boolean flag = true;
      while (flag) {
        String str = buf.readLine();// 不断接收输入的消息
        if ("exit".equals(str)) { // 客户端结束输入
          flag = false;
        } else {
          out.println("echo:" + str);
        }
      }
      out.close();
      client.close();
    }
    server.close();
  }


// 客户端程序
  @Test
  public void testClient2() throws IOException {
    // 创建客户端连接,指定服务器的域名和端口
    Socket client = new Socket("localhost", 8888);
    // 接收服务器端的输入
    BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
    // 从键盘接收输入
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    // 向服务器端输出消息
    PrintStream out = new PrintStream(client.getOutputStream());
    boolean flag = true;
    while (flag) {
      System.out.print("请输入消息:");
      String msg = reader.readLine();// 从键盘接收输入的数据
      out.println(msg); // 输出到服务器端
      if ("exit".equals(msg)) {
        flag = false;
      } else {
        System.out.println(buf.readLine()); // 输出服务器返回的消息
      }
    }
    client.close();
    reader.close();
    buf.close();
  }

首先运行服务端程序,然后运行客户端程序,观察控制台的运行结果:

服务端控制台输出:
======服务器已经运行,等待客户端的链接:======
建立客户端连接
======服务器已经运行,等待客户端的链接:======
建立客户端连接
======服务器已经运行,等待客户端的链接:======


客户端1控制台:
请输入消息:你好,追梦Java
echo:你好,追梦Java
请输入消息:hello,java
echo:hello,java
请输入消息:exit


客户端2控制台:
请输入消息:客户端2
echo:客户端2
请输入消息:hello,客户端2
echo:hello,客户端2
请输入消息:exit

以上的程序结果来看,所有的客户端输入的内容,都会在服务器端加上“echo:”后回显给客户端,同时当一个客户端退出时,服务器端没有退出,而是会等待下一个客户端进行连接,服务器端可以重复执行。

但是在以上的程序中存在一个服务器端单线程处理的问题,也就是说以上的程序服务器端每次只能有一个客户端连接,其他的客户端只有等前面的客户端退出后,自己才能执行。

可以通过上面的程序,首先启动服务端,再启动一个客户端,然后再启动一个客户端的过程来感受。

4

多线程机制下的 TCP 编程

503892ce064704096864df9340234440.png

上面程序中存在服务器单线程服务的问题,由于 accept() 方法的阻塞机制,因此当客户端没有退出时,一个服务器只能处理已经连接上的客户端程序,在这个客户端没有退出前,其他客户端是无法连接的。

为了保证服务器可以同时连接多个客户端,可以在服务器端的程序里加上多线程机制,也就是说,每个客户端连接之后都启动一个线程,这样一个服务器就可以同时支持过个客户端连接了。

c632f87afd6b1d5c1582c608ad6bd1f3.png

在服务器端加上多线程机制,就是需要在每个客户端连接之后启动一个新的线程。

因此,我们需要建立一个专门用于处理多线程操作的 EchoThread 类,实现 Runnable 接口。

public class EchoThread implements Runnable {


  private Socket client = null;


  public EchoThread(Socket client) {
    this.client = client;
  }


  @Override
  public void run() {
    PrintStream out = null;
    BufferedReader buf = null;
    try {
      // 得到客户端的输入信息
      buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
      // 实例化客户端的输出流
      out = new PrintStream(client.getOutputStream());
      boolean flag = true; // 表示一个客户端是否结束
      while (flag) {
        String str = buf.readLine();
        if ("exit".equals(str)) {
          flag = false;
        } else {
          out.println("echo:" + str);
        }
      }
      out.close();
      buf.close();
      client.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


}

下面在服务器程序 EchoThreadServer 类加上多线程的调用,使用 EchoThread 类:

public class EchoThreadServer {
  public static void main(String[] args) throws IOException {
    ServerSocket server = new ServerSocket(8888);
    Socket client = null;
    boolean flag = true;
    while (flag) {
      System.out.println("======服务器已经运行,等待客户端的链接:======");
      client = server.accept();
      // 启动一个EchoThread线程来处理一个独立的客户单
      new Thread(new EchoThread(client)).start();
    }
    server.close();
  }
}

客户端程序不变,首先运行服务端程序 EchoThreadServer,然后分别运行客户端程序两次,观察控制台的运行结果:

服务端控制台输出:
======服务器已经运行,等待客户端的链接:======
建立客户端连接
建立客户端连接


客户端1控制台:
请输入消息:我是客户端1
echo:我是客户端1
请输入消息:hello1
echo:hello1
请输入消息:exit


客户端2控制台:
请输入消息:我是客户端2
echo:我是客户端2
请输入消息:hello2
echo:hello2
请输入消息:exit

从上面的例子可以看出,服务端支持多个客户端的连接,并且每个客户端都可以独立与服务端进行交互,互不影响。

通过上面的介绍,对 Java 的网络编程有了进一步的认识,后面会基于以上的内容结合 Java IO 流来实现一个聊天室的功能,敬请期待......

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1922610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

压缩pdf文件的大小,pdf档怎么压缩为最小内存

在现代工作和学习中,pdf文件已经成为了一种不可或缺的文件格式。它跨平台、保持格式不变的优势使其在文件传输和分享中占据了重要位置。然而,pdf文件往往因为包含大量图像和文本而体积较大,这给文件的传输和存储带来了不少困扰。本文将为你介…

不会编程怎么办?量化交易不会编程可以使用吗?

量化交易使用计算机模型程序代替人工进行交易,一般需要投资者自己编写程序建模,然后回测无误之后再进行实盘交易,那么不会编程的投资者能使用量化软件进行量化交易吗? 不会编程使用量化软件有两种方法 一种是请人代写代码&#x…

Java高频面试基础知识点整理8

干货分享,感谢您的阅读!背景​​​​​​高频面试题基本总结回顾(含笔试高频算法整理) 最全文章见:Java高频面试基础知识点整理 (一)Java基础高频知识考点 针对人员: 1.全部人员都…

【三维AIGC】扩散模型LDM辅助3D Gaussian重建三维场景

标题:《Sampling 3D Gaussian Scenes in Seconds with Latent Diffusion Models》 来源:Glasgow大学;爱丁堡大学 连接:https://arxiv.org/abs/2406.13099 提示:写完文章后,目录可以自动生成,如何…

Guava LocalCache源码分析:LocalCache生成

Guava LocalCache源码分析:Cache生成 版本LocalCache参数说明Cache构建过程LocalCache介绍LocalCache实例化将builder中的属性赋值到LocalCache中分段 LocalCache为guava本地缓存的解决方案,提供了基于容量,时间和引用的缓存回收方式&#xf…

Spring MVC入门3

看完这篇博客你能学到什么 理解JSON的使用理解注解PathVariable理解解注解RequestPart理解cookie和Session的基本概念理解cookie和Session的区别 如果想真正掌握,还需要自己勤加练习。 正文 JSON JSON概念 JSON:JavaScript Object Notation 【JavaS…

免费长效IP在业务场景中的深度应用解析

在数字化竞赛的跑道上,每一秒的都至关重要,而免费长效IP,不仅为企业减少了运营成本,更在数据安全与访问效率上筑起了一道坚实的保护线。然而,面对市场上琳琅满目的代理服务,如何挑选出能应对各种业务场景的…

【JAVA poi-tl-ext 富文本转word】

富文本转word 环境使用poi-tl-ext的原因富文本转word代码 环境 jdk 1.8 <dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.16</version> </dependency>poi-tl-ext已经包…

ES 慢上游响应问题优化在用户体验场景中的实践

在抖音亿级日活流量的情况下&#xff0c;每天收到的用户反馈也是大量的&#xff0c;而用户反馈对于产品的发展与未来是至关重要的&#xff0c;因此用户体验管理平台&#xff08;简称VoC&#xff09;就应运而生&#xff0c;VoC 平台旨在通过技术平台化的方式&#xff0c;结合反馈…

字体反爬之自动化通过字体文件生成映射字典

1、首先找到以.ttf结尾的字体文件&#xff0c;下载下来&#xff0c;以我的字体文件sfont.ttf为例 sont.ttf下载地址https://download.csdn.net/download/lingyingdon/89534953 目前只测试了.ttf文件。如果想使用woff字体文件&#xff0c;请自行测试 2、下载分割字体文件的软件…

从汇编层看64位程序运行——参数传递的底层实现

大纲 小于等于6个参数一个参数总结 两个参数总结 三个参数总结 四个参数总结 五个参数总结 六个参数总结 大于6个参数七个参数总结 在32位系统中&#xff0c;参数的传递主要依靠栈来进行。那么64位系统上&#xff0c;是否依旧符合这个规则呢&#xff1f;答案是“不是”。64位系…

Objective-C 自定义渐变色Slider

文章目录 一、前情概要二、具体实现 一、前情概要 系统提供UISlider&#xff0c;但在开发过程中经常需要自定义&#xff0c;本次需求内容是实现一个拥有渐变色的滑动条&#xff0c;且渐变色随着手指touch的位置不同改变区域&#xff0c;类似如下 可以使用CAGradientLayer实现渐…

【Linux】Linux操作系统

Linux基本指令 os概念与定位 本节内容&#xff1a; Linux操作系统讲解 os概念与定位 操作系统&#xff08;Operating System&#xff0c;简称OS&#xff09;是管理和控制计算机硬件与软件资源的计算机程序。总的来讲&#xff0c;操作系统是一款做软硬件管理的软件。 了解操作…

springBoot(若依)集成camunda

1、下图为项目结构 2、最外层 pom引入依赖 <properties><!--camunda 标明版本&#xff0c;注意要个自己的Spring 版本匹配&#xff0c;匹配关系自行查询官网--><camunda.version>7.18.0</camunda.version> </properties> 3、common模块引入依赖 …

安卓14中Zygote初始化流程及源码分析

文章目录 日志抓取结合日志与源码分析systemServer zygote创建时序图一般应用 zygote 创建时序图向 zygote socket 发送数据时序图 本文首发地址 https://h89.cn/archives/298.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 本文主要结合日志和代码看安卓 14 中 Zy…

抗量子密码算法:保障未来信息安全的新盾牌

随着量子计算的迅猛发展&#xff0c;传统加密算法正面临着前所未有的挑战。量子计算机利用量子比特的特殊性质&#xff0c;能在极短时间内破解目前广泛使用的公钥加密体系&#xff0c;如RSA、ECC等。这使得我国及全球的信息安全体系遭受严重威胁。为了应对这一挑战&#xff0c;…

知识图谱入门笔记

自学参考&#xff1a; 视频&#xff1a;斯坦福CS520 | 知识图谱 最全知识图谱综述 详解知识图谱的构建全流程 知识图谱构建&#xff08;概念&#xff0c;工具&#xff0c;实例调研&#xff09; 一、基本概念 知识图谱&#xff08;Knowledge graph&#xff09;&#xff1a;由结…

基于LSTM的局部特征提取网络算法原理

目录 一、LSTM的基本原理与结构 1. LSTM的核心结构 2. LSTM的工作原理 二、基于LSTM的局部特征提取 1. 输入处理与序列表示 2. LSTM层处理与特征提取 3. 特征提取的优势与应用 三、实现细节与注意事项 1. 数据预处理 2. 网络结构与参数选择 3. 训练策略与正则化 4.…

Chapter 1:数据结构前言

在数字化的世界里&#xff0c;我们每天都在与数据打交道。然而&#xff0c;你是否曾想过&#xff0c;这些数据是如何被组织、存储和处理的&#xff1f;这就是数据结构的魅力所在。 数据结构&#xff0c;简单来说&#xff0c;就是数据的组织方式。它决定了我们如何高效地访问和操…

【游戏客户端】大话slg玩法架构(三)建筑控件

【游戏客户端】大话slg玩法架构&#xff08;三&#xff09;建筑控件 大家好&#xff0c;我是Lampard家杰~~ 今天我们继续给大家分享SLG玩法的实现架构&#xff0c;关于SLG玩法的介绍可以参考这篇上一篇文章&#xff1a;【游戏客户端】制作率土之滨Like玩法 PS&#xff1a;和之前…