【Java基础教程】(四十七)网络编程篇:网络通讯概念,TCP、UDP协议,Socket与ServerSocket类使用实践与应用场景~

news2024/11/26 11:52:58

Java基础教程之网络编程

  • 🔹本节学习目标
  • 1️⃣ 网络编程的概念
      • 🔍 TCP和 UDP协议
  • 2️⃣ Socket 与ServerSocket 类
  • 3️⃣ 网络编程实战——Echo 程序
  • 4️⃣ 应用场景
  • 🌾 总结

在这里插入图片描述

🔹本节学习目标

  • 了解多线程与网络编程的操作关系;
  • 了解网络程序开发的主要模式;
  • 了解 TCP 程序的基本实现;

1️⃣ 网络编程的概念

在Java中,网络编程的核心意义是实现不同电脑主机之间的数据交互。Java采用了一种简化的概念,将这个过程进一步抽象为JVM(Java虚拟机)进程之间的通信。可以在同一台电脑上同时运行多个JVM进程,而这些不同的JVM进程能够相互通信,它们在网络编程中被视为不同的主机。

图1 远程访问——不同的JVM进程间的访问

每个JVM进程都有自己的内存空间和资源,并且可以在不同的物理机或同一台物理机上运行。通过使用Java提供的网络编程API,我们可以在这些JVM进程之间建立连接、发送和接收数据。

这种以JVM进程划分网络的方式带来了一些优势。首先,与传统的网络编程相比,它提供了更高层次的抽象,使得开发人员可以更方便地处理网络通信。其次,由于JVM进程可以在同一台物理机上运行,它们之间的通信速度更快,并且可以共享某些资源,从而提高了效率。

此外,我们前面介绍过的多线程篇中,也提供了一些并发编程的机制,如线程和锁,使得多个JVM进程之间的数据交互更加安全可靠。

而不同JVM进程间,彼此的数据访问也属于远程访问。在Java中存在的远程方法调用(Remote Method Invocation,RMI) 技术或企业 JavaBean(Enterprise JavaBean, EJB) 也都是依靠此概念进行使用的。

网络编程的实质意义在于数据的交互,而在交互过程中一定就会分为服务器端与客户端,而这两端的开发就会存在以下两种模式。

  • C/S 结构 (Client / Server), 此类模式的开发一般要编写两套程序,一套是客户端代码,另外一套属于服务器端代码。由于需要有编写程序,所以对于开发以及维护的成本较高。但是由于其使用的是自己的连接端口与交换协议,所以安全性比较高。而 C/S 结构程序的开发分为两种:
    • TCP (传输控制协议,可靠的传输);
    • UDP (数据报协议)。
  • B/S 结构 (Browser /Server), 不再单独开发客户端代码,只开发一套服务器端程序,客户端将利用浏览器进行访问,这种模式只需要开发一套程序,但是安全性不高,因为使用的是公共的 HTTP 协议以及公共的80端口。

ASPPHPJSP 等都属于 B/S 的常见开发技术,这些都需要单独的服务器支持。而这些 B/S 技术要想实现互相访问,则需要Web Service技术支持。

🔍 TCP和 UDP协议

  1. TCP(传输控制协议)
  • TCP提供了可靠的、面向连接的通信。它通过建立一个持久的连接,在发送数据之前,会先进行握手过程来确保双方的通信正常。这种可靠性是通过使用确认机制和重传机制来实现的,因此在传输过程中,既可以保证数据的顺序不变,也可以确保数据不被丢失或损坏。
  • TCP适合于需要确保数据完整、按照顺序到达的应用场景,例如文件传输、HTTP请求等。

  1. UDP(用户数据报协议)
  • UDP是一种无连接的协议,它不需要先建立连接就能直接发送数据。这意味着它没有像TCP那样的握手和断开连接的开销,具有较低的延迟和网络负载。
  • UDP提供了一种快速而简单的数据传输方式,但不保证数据的可靠性和顺序性。由于缺乏确认机制和重传机制,并且数据包可能在传输过程中丢失或乱序,所以需要应用层来处理可靠性和有序性的问题。
  • UDP适用于那些对实时性要求较高,但对数据完整性和顺序性要求相对较低的应用场景,例如音视频传输、实时游戏等。

在网络编程中,开发人员可以根据具体的业务需求选择使用TCPUDP。如果需要确保可靠的数据传输和有序性,则选择TCP;如果追求更低的延迟并能容忍一些数据丢失或乱序的情况,则可以选择UDP。在某些情况下,也可以同时使用两种协议来处理不同类型的数据。

2️⃣ Socket 与ServerSocket 类

java.net 包提供了网络编程有关的开发工具类,在此包中有以下两个主要的核心操作类。

  • ServerSocket 类:是一个封装支持 TCP 协议的操作类,主要工作在服务器端,用于接收客户端请求;
  • Socket 类:也是一个封装了 TCP 协议的操作类,每一个Socket 对象都表示一个客户端。

下面列出了ServerSocket 类的常用操作方法:

方法名称类型描述
public ServerSocket(int port) throws IOException构造开辟一个指定的端口监听, 一般使用 5000以上的端口
public Socket accept() throws IOException普通服务器端接收客户端请求,通过Socket 返回
public void close() throws IOException普通关闭服务器端

下面列出了Socket 类的常用操作方法:

方法名称类型描述
public Socket(String host, int port) throws UnknownHostException, IOException构造指定要连接的主机(IP地址)和端口
public OutputStream getOutputStream() throws IOException普通取得指定客户端的输出对象,使用 PrintStream操作
public InputStream getInputStream() throws IOException普通从指定的客户端读取数据,使用 Scanner操作

在客户端,程序可以通过 Socket 类的 getInputStream()方法,取得服务器的输出信息,在服务器端可以通过 getOutputStream() 方法取得客户端的输出信息,如下所示。

图2 客户端与服务器端交互

在进行网络程序的开发中,最为重要的就是服务器端的功能。下边范例操作定义的服务器端将针对连接的客户端发出一个 “Hello World” 的字符串信息。

//	范例 1: 定义服务器端——主要使用 ServerSocket
package com.xiaoshan.demo;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class HelloServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(9999);    //所有的服务器必须有端口
		System.out.println("等待客户端连接…");            //提示信息
		Socket client = server.accept();                                       //等待客户端连接
		//OutputStream并不方便进行内容的输出,所以利用打印流完成输出
		PrintStream out = new PrintStream(client.getOutputStream());
		out.println("Hello World !");                                               //输出数据
		out.close();
		client.close();
		server.close();
	}
}

程序执行结果:

等待客户端连接…

从程序执行结果可以看到,程序将出现阻塞情况, 一直到客户端连接后才会继续执行。

此程序在本机的9999端口上设置了一个服务器的监听操作,accept()方法表示打开服务器监听,这样当有客户端通过 TCP 连接方式连接到服务器端后,服务器端将利用 PrintStream 输出数据,当数据输出完毕后该服务器端就将关闭,所以本次定义的服务器只能处理一次客户端的请求。

//	范例 2: 编写客户端——Socket
package com.xiaoshan.demo;

import java.net.Socket;
import java.util.Scanner;

public class HelloClient {
	public static void main(String[] args) throws Exception {
		Socket client = new Socket("localhost", 9999);                       //连接服务器端
		//取得客户端的输入数据流对象,表示接收服务器端的输出信息
		Scanner scan = new Scanner(client.getInputStream());		//接收服务器端回应数据
		scan.useDelimiter("\n");	//设置分隔符
		if (scan.hasNext()){	//是否有数据
			System.out.println("【回应数据】"+ scan.next());	//取出数据
		}
		scan.close();
		client.close();
	}
}

程序执行结果:

【回应数据】Hello World !

在 TCP 程序中,每一个 Socket 对象都表示一个客户端的信息,所以客户端程序要连接也必须依靠 Socket 对象操作。在实例化 Socket 类对象时必须设置要连接的主机名称(本机为 localhost,或者填写 IP 地址)以及连接端口号,当连接成功后就可以利用 Scanner 进行输入流数据的读取,这样就可以接收服务器端的回应信息。

3️⃣ 网络编程实战——Echo 程序

在网络编程中 Echo 是一个经典的程序开发模型,程序实现的功能:客户端随意输入信息并且将信息发送给服务器端,服务器端接收后前面加上一 个 “ECHO:” 的前缀标记后将数据返还给客户端。在本程序中服务器端既要接收客户端发送来的数据,又要向客户端输出数据,同时考虑到需要进行多次数据交换,所以每次连接后不应该立刻关闭服务器,而当用户输入了一些特定字符串 (例如:“byebye”) 后才表示可以结束本次的 Echo 操作。

//	范例 3: 实现服务器端
package	com.xiaoshan.demo;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class EchoServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server = new ServerSocket(9999);		// 定义连接端口
		Socket client = server.accept();             	//等待客户端连接
		//得到客户端输入数据以及向客户端输出数据的对象,利用扫描流接收,打印流输出
		Scanner scan = new Scanner(client.getInputStream());
		PrintStream out = new PrintStream(client.getOutputStream());
		boolean flag = true;                          	//设置循环标记
		while(flag){
			if (scan.hasNext()){				//是否有内容输入
				String str = scan.next().trim();	//得到客户端发送的内容,并删除空格
				if(str.equalslgnoreCase("byebye")){ 	//程序结束标记
					out.println("拜拜,再见!"); 		//输出结束信息
					flag = false;					//退出循环
				}else{
					out.println("ECHO:" + str);		//回应输入信息,加“ECHO:” 前缀返回
				}
			}
		}
		scan.close();
		out.close();
		client.close();
		server.close();
	}
}

由于服务器端需要接收以及回应客户端的请求,所以在程序开始就首先取得了客户端的输入流与输出流,同时为了方便数据的读取与输出,分别使用了 ScannerPrintStream 进行 IO 的操作包装。考虑到该服务器端需要与客户端进行重复的数据交互,所以使用了一个 while 循环来不断实现数据的接收与输出。

//	范例 4: 定义客户端
package com.xiaoshan.demo;

import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class EchoClient {
	public static void main(String[] args) throws Exception{
		Socket client = new Socket("localhost", 9999);       //服务器地址与端口
		Scanner input = new Scanner(System.in);                         //键盘输入数据
		//利用Scanner包装客户端输入数据(服务器端输出),PrintStream包装客户端输出数据
		Scanner scan = new Scanner(client.getInputStream());
		PrintStream out = new PrintStream(client.getOutputStream());
		input.useDelimiter("\n");	//设置键盘输入分隔符
		scan.useDelimiter("\n");	//设置回应数据分隔符
		boolean flag = true;		//循环标志
		while (flag){
			System.out.print("请输入要发送数据:");
			if(input.hasNext()){		//键盘是否输入数据
				String str = input.next().trim();	//取得键盘输入数据
				out.println(str);		//发送数据到服务器端
				if(str.equalsIgnoreCase("byebye"){ 	//结束标记
					flag = false;		//结束循环
				}
				if (scan.hasNext()){		//服务器端有回应 
					System.out println(scan.next());	//输出回应数据
				}
			}
		}
		input.close();
		scan.close();
		out.close();
		client.close();
	}
}

程序实现了键盘数据的输入与发送操作,每当用户输入完信息后会将该信息发送到服务器端,只要发送的数据不是 “byebye” ,服务器端都会将发送的数据处理后再发送回客户端。由于需要重复输入, 所以在客户端上也使用了一个 while 循环进行控制。

范例4 就实现了一个最简单的服务器端与客户端通讯,但是该程序只能连接一个客户端,不能连接其他客户端,因为所有的操作都是在主线程上进行的开发,也就是说该程序属于单线程的网络应用。而在实际的开发中一个服务器需要同时处理多个客户端的请求操作,在这样的情况下就可以利用多线程来进行操作,把每一个连接到服务器端的客户都作为一个独立的线程对象保留,如下所示。

图3 多线程优化网络编程

//	范例 5:修改服务器端
package com.xiaoshan.demo;

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

class EchoThread implements Runnable { 	//建立线程类
	private Socket client;		//每个线程处理一个客户端
	
	public EchoThread(Socket client){	//创建线程对象时传递 Socket
		this.client = client;
	}
	
	@Override
	public void run(){
		try {			//每个线程对象取得各自Socket的输入流与输出流
			Scanner scan = new Scanner(client.getInputStream());
			PrintStream out = new PrintStream(client.getOutputStream());
			boolean flag = true;
			while(flag){		//控制多次接收操作
				if(scan.hasNext()){		//是否有内容
					String str = scan.next().trim();	//得到客户端发送的内容
					if (str.equalsIgnoreCase("byebye")){	// 程序结束
						out.println("拜拜,再见!");
						flag = false;				//退出循环
					}else{
						out.println("ECHO:"+str);	//回应信息
					}
				}
			}
			scan.close();
			out.close();
			client.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}
}

public class EchoServer {
	public static void main(String[] args) throws Exception{
		ServerSocket server = new ServerSocket(9999);	//在9999端口上监听
		boolean flag = true;                        	//循环标记
		while(flag){                           	//接收多个客户端请求
			Socket client = server.accept();          	//客户端连接
			new Thread(new EchoThread(client)).start();	//创建并启动新线程
		}
		server.close();
	}
}

程序使用了多线程的概念来处理每一个客户端的请求,这样服务器就可以同时处理多个客户端的连接操作。当有新的客户端连接到服务器端后,会启动一个新的线程,这样在此线程中就会各自处理每 一个客户端的输入与输出操作。

4️⃣ 应用场景

Java网络编程具有广泛的应用场景,包括但不限于以下几个方面:

  1. 客户端-服务器通信:Java网络编程可以用于构建基于C/S架构的分布式系统。通过建立连接、传输数据和响应请求,可以实现客户端与服务器之间的通信,例如网站服务器与浏览器之间的HTTP通信、即时通讯程序等。

  2. 文件传输和共享:Java网络编程可以用于文件传输和共享。通过建立TCP连接,可以在客户端和服务器之间传输文件,如FTP(文件传输协议)或SFTP(SSH文件传输协议)。

  3. 远程过程调用(RPC):Java网络编程可以支持远程过程调用,使得应用程序可以在不同的主机上相互调用函数或方法。这种方式可以实现分布式计算和服务架构,常见的例子是使用Java RMI(远程方法调用)、Apache Thrift、gRPC等。

  4. Socket编程:Java网络编程中的Socket API允许开发者直接控制网络连接和数据传输。可以创建基于TCP或UDP的Socket,实现点对点的通信,例如实时游戏、聊天应用等。

  5. 网络爬虫和数据采集:Java网络编程提供了强大的能力来进行网页抓取和数据采集。通过使用网络库和相关API,可以模拟浏览器行为,发送HTTP请求、解析HTML、提取数据并进行存储和分析。

总之,Java网络编程广泛应用于各种场景,包括网络服务端开发、网页爬取、远程过程调用、文件传输和Socket通信等。通过利用Java提供的网络API和库,开发人员可以构建高效、可靠的网络应用程序和分布式系统。

🌾 总结

本文介绍了网络编程的概念以及与之相关的TCPUDP协议。我们探讨了Java中的 SocketServerSocket类,这些重要的API用于实现网络通信。通过一个简单的实战示例——Echo程序,我们展示了如何使用Java进行网络编程,并解释了其工作原理。

网络编程在现代应用开发中扮演着至关重要的角色。它允许不同主机之间的数据交互,使得分布式系统成为可能。同时,网络编程也适用于各种场景,包括客户端-服务器通信、网络爬虫和数据采集、文件传输和共享、远程过程调用等。

无论是构建一个Web应用程序还是设计一个分布式系统,掌握网络编程技能都是至关重要的。Java提供了强大而丰富的网络编程库和API,支持多种协议和通信方式。通过理解并灵活运用这些概念和工具,开发人员可以轻松构建高性能、可靠的网络应用程序,并满足不同应用场景的需求。


温习回顾上一篇(点击跳转)
《【Java基础教程】(四十六)IO篇 · 下:System类对IO的支持:错误输出、信息输出、系统输入,字符缓冲流、扫描流和对象序列化流~》

继续阅读下一篇(点击跳转)
《【Java基础教程】(四十)集合体系篇:》

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

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

相关文章

JavaWeb黑马程序员——数据库相关概念

本文前提 这是黑马程序员JavaWeb视频里的笔记资源,我只是照搬过来以便于日后复习。 视频链接:https://www.bilibili.com/video/BV1Qf4y1T7Hx?p3&vd_sourced4bb8e491e28c81c780f2db9d6123a41 MySQL基础 今日目标: 完成MySQL的安装及登…

如何利用JMeter测试带有Token参数的POST接口

JMeter有一个很强大的功能就是可以用来做接口测试。 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系…

如何将路径字符串数组(string[])转成树结构(treeNode[])?

原文链接:如何将路径字符串数组(string[])转成树结构(treeNode[])? 需求 这里的UI使用的是Element-Plus。 将一个路径字符串数组(当然也可能是其他目标字符串数组),渲染成树。 /*source:/a/b/c/d/e/a/b/e/f/g/a/b/h/a…

深入浅出对话系统——闲聊对话系统

引言 闲聊对话系统也很多别名 聊天机器人ChatbotSocial ChatbotChit-chat botConversational AI开放领域对话系统 实现方法 现在闲聊对话系统一般有两种主要的实现方法 检索式对话系统生成式对话系统 可以任务闲聊对话系统也是一个函数 y f ( x ) yf(x) yf(x)&#xff0…

6-Linux的磁盘分区和挂载

Linux的磁盘分区和挂载 Linux分区查看所有设备的挂载情况 将磁盘进行挂载的案例增加一块磁盘的总体步骤1-在虚拟机中增加磁盘2- 分区3-格式化分区4-挂载分区5-进行永久挂载 磁盘情况查询查询系统整体磁盘使用情况查询指定目录的磁盘占用情况 磁盘情况-工作实用指令统计文件夹下…

【Docker】Docker网络之五大网络模式

Docker网络 1.Docker网络2.Docker的网络模式3.网络模式详解3.1 host模式3.2 container模式3.3 none模式3.4 bridge模式3.5 自定义网络模式 4.docker网络模式知识点总结 1.Docker网络 Docker网络实现原理 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(dock…

Cesium态势标绘专题-圆角矩形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

RNN架构解析——传统RNN模型

目录 传统RNN的内部结构图使用RNN优点和缺点 传统RNN的内部结构图 使用RNN rnnnn.RNN(5,6,1) #第一个参数是输入张量x的维度,第二个是隐藏层维度,第三层是隐藏层的层数 input1torch.randn(1,3,5) #第一个是输入序列的长度,第二个是批次的样本…

FPGA设计时序分析二、建立/恢复时间

目录 一、背景知识 1.1 理想时序模型 1.2 实际时序模型 1.2.1 时钟不确定性 1.2.2 触发器特性 二、时序分析 2.1 时序模型图 ​2.2 时序定性分析 一、背景知识 之前的章节提到,时钟对于FPGA的重要性不亚于心脏对于人的重要性,所有的逻辑运算都离开…

[start] m40 test

software & update 470 drive version # cd /etc/apt # mv sources.list sources.list.bak # sudo vi /etc/apt/sources.list # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释 deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ ja…

供应商管理平台:高效整合资源,提升供应链效能

随着全球市场竞争的不断升级,企业对供应商管理的重要性越来越重视。而供应商管理平台作为一种高效整合资源、提升供应链效能的工具,对于企业来说意义深远。本文将围绕供应商管理平台的概念、优势以及应用,探讨其在提升供应商管理和优化供应链…

面向对象编程:多态性的理论与实践

文章目录 1. 修饰词和访问权限2. 多态的概念3. 多态的使用现象4. 多态的问题与解决5. 多态的意义 在面向对象编程中,多态是一个重要的概念,它允许不同的对象以不同的方式响应相同的消息。本文将深入探讨多态的概念及其应用,以及在Java中如何实…

Docker 网络端口映射 四大网络模式

Docker 网络端口映射 Docker 网络实现原理 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网…

爆肝整理,接口测试方法总结+常问面试题(答案)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 理想的测试流程 …

flask路由添加参数

flask路由添加参数 在 Flask 中,可以通过两种方式在路由中添加参数:在路由字符串中直接指定参数,或者通过 request 对象从请求中获取参数。 在路由字符串中指定参数:可以将参数直接包含在路由字符串中。参数可以是字符串、整数、…

8-js高级-6(promise)

一 Promise 的理解和使用 1 Promise 是什么? 理解 抽象表达: Promise 是一门新的技术(ES6 规范)Promise 是 JS 中进行异步编程的新解决方案 (备注:旧方案是单纯使用回调函数) 具体表达: 从语法上来说: Promise 是一个构造函数从功能上来说: promise 对象用来…

vue3 实现排序按钮

需求背景解决效果index.vue 需求背景 需要实现一个复用性&#xff0c;是提供表单顺倒排序的按钮 解决效果 index.vue <!--/*** author: liuk* date: 2023/7/25* describe: 排序按钮*/--> <template><div class"sort-fn"><span :class"[…

记一次完整体系的攻防演练

准备工作&#xff1a; 1&#xff0c;在客户的内网环境部署一个Windows7系统&#xff0c;在这个系统上把finecms这个应用部署上去。把finecms安装之后&#xff0c;和客户沟通&#xff0c;把这个应用的地址映射到公网上去。 2&#xff0c;其次&#xff0c;没有条件的话&#xff0…

蓝桥杯上岸必背!!!(第七期 最短路算法)

第七期&#xff1a;最短路算法&#x1f525; &#x1f525; &#x1f525; 蓝桥杯热门考点模板总结来啦✨ 你绝绝绝绝绝绝对不能错过的常考最短路算法模板 &#x1f4a5; ❗️ ❗️ ❗️ 大家好 我是寸铁✨ 还没背熟模板的伙伴们背起来 &#x1f4aa; &#x1f4aa; &…

SpringBoot整合JavaMail

SpringBoot整合JavaMail 简单使用-发送简单邮件 介绍协议 导入坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>添加配置 spring:mail:host: smtp.qq.co…