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

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

1

UDP 介绍

d665f616a4c6db554d837097c15c5958.png

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

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

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

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

// 客户端发送消息
@Testpublic void testClient() throws IOException {// 1、创建客户端指定端口为6666DatagramSocket 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();}// 服务端接收消息
@Testpublic void testServer() throws IOException {// 1、创建服务端指定端口为8888DatagramSocket 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 岁。

// 客户端发送数据
@Testpublic void client() throws IOException {// 1、创建客户端指定端口为6666DatagramSocket 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;}// 服务端接收数据
@Testpublic void server() throws IOException {// 1、创建服务端指定端口为8888DatagramSocket 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,欢迎使用!”,客户端接收服务端的数据并输出到控制台。

// 服务器端程序@Testpublic 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();}
// 客户端程序@Testpublic 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”时退出连接。

// 服务器端程序
@Testpublic 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();}// 客户端程序@Testpublic 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;}@Overridepublic 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.mzph.cn/news/871035.shtml

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

相关文章

css横向滚动条支持鼠标滚轮

在做视频会议的时候&#xff0c;标准模式视图会有顶部收缩的一种交互方式&#xff0c;用到了横向滚动&#xff1b;一般情况下鼠标滚轮只支持竖向滚动&#xff0c;这次写个demo是适配横向滚动&#xff1b; 效果图展示 实现横向滚动条顶部显示 <div className{style.remote_u…

【YOLO格式的数据标签,目标检测】

标签为 YOLO 格式&#xff0c;每幅图像一个 *.txt 文件&#xff08;如果图像中没有对象&#xff0c;则不需要 *.txt 文件&#xff09;。*.txt 文件规格如下: 每个对象一行 每一行都是 class x_center y_center width height 格式。 边框坐标必须是 归一化的 xywh 格式&#x…

nginx正向代理和反向代理

nginx正向代理和反向代理 正向代理以及缓存配置 代理&#xff1a;客户端不再是直接访问服务器&#xff0c;通过代理服务器访问服务端。 正向代理&#xff1a;面向客户端&#xff0c;我们通过代理服务器的IP地址访问目标服务端。 服务端只知道代理服务器的地址&#xff0c;真…

每日一练 - OSPF邻居关系建立故障排查

01 真题题目 OSPF邻居关系建立出现故障&#xff0c;通过display ospf error命令查看&#xff0c;显示如下信息&#xff0c;则邻居建立失败的原因可能是&#xff1a; A. Router ID冲突 B.区域ID不匹配 C.网络掩码不一致 D.MTU不一致 02 真题答案 B 03 答案解析 从图片中可以…

数据丢失?不存在的!

今年3月份&#xff0c;AT&T遭遇了严重的数据泄露事件&#xff0c;导致7300万客户账户信息被泄露。泄露的信息包括客户的姓名、电话号码、邮寄地址等敏感资料&#xff0c;甚至部分客户的加密密码也被泄露&#xff0c;使得约760万AT&T用户的账户面临被劫持的风险。 此次…

博客文章多平台发布工具

做过博客分享和自媒体的同学应该都知道&#xff0c;在多个平台上同步发布、更新自己的原创内容&#xff0c;是快速传播知识、提高用户触达率的有效方式。 然而&#xff0c;一篇文章要在N个平台上重复进行编辑、排版、图片/视频上传的苦恼&#xff0c;你一定经历过吧&#xff1…

从汇编层看64位程序运行——静态分析和动态分析入门

大纲 GDBIDA总结参考资料 之前一直谈各种相对宏观的工具怎么使用&#xff0c;比如Flink、RabbitMQ等。最近想聊聊比较微观的技术&#xff0c;用各种“显微镜”去看看运行在系统层的二进制码是什么样子。当然二进制码比较难以记忆&#xff0c;于是我会从二进制码的助记符——汇编…

408数据结构-图的应用2-最短路径 自学知识点整理

前置知识&#xff1a;最小生成树&#xff0c;图的遍历 最短路径 当图是带权图时&#xff0c;把从一个顶点 v 0 v_0 v0​到图中其余任意一个顶点 v i v_i vi​的一条路径所经过边上的权值之和&#xff0c;定位为该路径的带权路径长度&#xff0c;把带权路径长度最短的那条路径&…

centOS79中安装redis7.0

##red## &#x1f534; 大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff0c;雄雄的小课堂。 一、前言 新服务器&#xff0c;一些环境是少不了要安装的&#xff0c;比如常见的redis&#xff0c;mysql&#xff0c;nginx等&#xff0c;今天&#xff0c;上次&a…

【Golang】map的使用

map声明的方式 //声明var m map[string]string//在使用map之前&#xff0c;先make&#xff0c;make的作用就是给map分配空间m make(map[string]string)m["lover"] "Yzx"m["friend1"] "Zxw"m["friend2"] "Zzc"…

C语言-gcc编译四步

gcc -E hello.c -o hello.i 预处理 gcc -S hello.i -o hello.s 编译 gcc -c hello.s -o hello.o 汇编 gcc hello.o -o hello.exe 链接

springboot增加过滤器后中文乱码

记录一下小问题 public class RepeatableHttpServletWrapper extends HttpServletRequestWrapper {private byte[] body;public RepeatableHttpServletWrapper(HttpServletRequest request) throws IOException {super(request);request.setCharacterEncoding("UTF-8&q…

泛微E-Cology WorkflowServiceXml SQL注入漏洞复现

0x01 产品简介 泛微e-cology是一款由泛微网络科技开发的协同管理平台,支持人力资源、财务、行政等多功能管理和移动办公。 0x02 漏洞概述 2024年7月,泛微官方发布了新补丁,修复了一处SQL注入漏洞。经分析,攻击者无需认证即可利用该漏洞,建议受影响的客户尽快修复漏洞。…

fortran快速排序算法,示例对一维数组进行排序

fortran快速排序算法&#xff0c;示例对一维数组进行排序 0. 引言1. 快速排序方法(QuickSqrt)代码实现2. 结语 0. 引言 快速排序&#xff08;QuickSort&#xff09;是一种常用的排序算法&#xff0c;采用分治策略实现。它的基本思想是通过一趟排序将待排序的数据分割成独立的两…

Lingo学习(一)——基本界面、解方程、变量

一、Lingo基本界面 【步骤】 1.双击打开Lingo 2.弹出一个对话框,点击Cancel左边的Never Register即可,其余内容用不到。 3:界面自动弹出名为“Lingo Model – Lingo 1”的窗口,用于书写代码。 4:以解方程的题目:x12为例,写完代码后,点击“红色的靶心”运行程序。 5:首先Lin…

第1章 初识 Express

1.1 什么是 Express Express 是一个简洁而灵活的 Node.js Web 应用框架&#xff0c;提供了一系列强大的特性用于开发 Web 和移动应用。它基于 Node.js 构建&#xff0c;并且与 Node.js 的非阻塞 I/O 模型无缝集成&#xff0c;使其非常适合于构建高性能的 Web 应用。 主要特点…

【办公软件】PPT使用轮子动画做圈动作

在实际的PPT制作中&#xff0c;我们可能会用到画圈的动作来强调重点。如下所示为最基础的画圈动作。 那么如何来做一个这样的动作呢&#xff1f; 首先在PPT中选择插入&#xff0c;选择形状椭圆 然后按Shift画图&#xff0c;即可画出一个正圆 然后使用绘图工具&#xff0c;将开关…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥证明介绍及算法规格】

密钥证明介绍及算法规格 HUKS为密钥提供合法性证明能力&#xff0c;主要应用于非对称密钥的公钥的证明。 基于PKI证书链技术&#xff0c;HUKS可以为存储在HUKS中的非对称密钥对的公钥签发证书&#xff0c;证明其公钥的合法性。业务可以通过系统提供的根CA证书&#xff0c;逐级…

Photoshop套索工具使用指南:解锁自由选区的艺术

在Adobe Photoshop的强大工具箱中&#xff0c;套索工具组是每位图像处理爱好者与专业人士的得力助手。这组工具&#xff0c;包括套索工具、多边形套索工具和磁性套索工具&#xff0c;为用户提供了高度灵活的选择区域方式&#xff0c;无论是处理复杂的图像边缘还是进行精细的抠图…

VMware安装Ubuntu以及利用vscode远程Ubuntu

一、VMware安装Ubuntu &#xff08;1&#xff09;VMware安装Ubuntu主要参考此文VMware虚拟机安装Ubuntu22.04图文教程&#xff08;超详细&#xff01;&#xff01;&#xff01;&#xff09;。 &#xff08;2&#xff09;VMware密钥参考此文24年VMware 17密钥(附下载链接&#…