JAVAEE值网络编程(2)_TCP流套接字及通信模型、TCP网络编程及代码实例

2024-06-06 12:20

本文主要是介绍JAVAEE值网络编程(2)_TCP流套接字及通信模型、TCP网络编程及代码实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

 在上一节内容中,我们介绍了什么是套接字,以及使用UDP数据报套接字网络编程, 最后我们还介绍了Java数据报套接字通信模型以及相关代码实例。在这一节我们将会介绍TCP流套接字编程。


一、流套接字及通信模型

1.1 TCP套接字

TCP,即Transmission Control Protocol(传输控制协议),是传输层协议。

TCP主要特点:(会在后续单独章节中详细介绍)

  1. 有连接
  2. 可靠传输
  3. 面向字节流
  4. 有接收、发送缓冲区
  5. 大小不限

对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

1.2 原始套接字

 原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

1.3 Java流套接字通信模型

在这里插入图片描述
注意事项:

  1. 客户端和服务端:开发时,经常是基于一个主机开启两个进程作为客户端和服务端,但真实的场景,一般都是不同主机。
  2. 注意目的IP和目的端口号,标识了一次数据传输时要发送数据的终点主机和进程
  3. Socket编程我们是使用流套接字和数据报套接字,基于传输层的TCP或UDP协议,但应用层协议也需要考虑。
  4. 端口占用问题:如果一个进程A已经绑定了一个端口,再启动一个进程B绑定该端口,就会报错,这种情况也叫端口被占用
  5. 解决端口被占用的问题:
    如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B;
    如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

二、TCP流套接字编程

2.1 ServerSocket API

ServerSocket 是创建TCP服务端Socket的API,ServerSocket构造方法如下:
在这里插入图片描述
 另外,ServerSocket 相关方法如下:
在这里插入图片描述

2.2 Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
 Socket的构造方法如下:
在这里插入图片描述
 Socket相关方法:
在这里插入图片描述

2.3 TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

短连接:每次接收到数据并返回响应后,都关闭连接,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,长连接可以多次收发数据。

TCP长短连接对比:

  1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

三、代码实例

3.1 TCP 客户端

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.ref.SoftReference;
import java.net.Socket;
import java.util.Scanner;/*** @author Zhang* @date 2024/5/2115:13* @Description:*/
public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIP,int serverPort) throws IOException {// 此时,需要在创建 Socket 的同时,和服务器”建立连接“,此时就得告诉 Socket 服务器在哪里socket = new Socket(serverIP,serverPort);}public void start(){Scanner scanner = new Scanner(System.in);try(InputStream  inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){PrintWriter writer = new PrintWriter(outputStream);Scanner scannerNetwork = new Scanner(inputStream);while (true){// 1. 从控制台读取用户输入的内容System.out.print("-->");String request = scanner.next();// 2. 把字符串作为请求,发给服务器//    这里用println,是为了让后后面请求带上换行,也就是和服务器读取请求,scanner.next 呼应writer.println(request);writer.flush();// 3. 读取服务器返回的响应String response = scannerNetwork.next();// 4. 在界面上显示内容System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);client.start();}
}

3.2 TCP 服务端

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @author Zhang* @date 2024/5/2115:13* @Description:*/
public class TcpEchoServer {private ServerSocket serverSocket  = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);// 指定端口号}public void start() throws IOException {System.out.println("服务器启动!");ExecutorService service = Executors.newCachedThreadPool();  //创建线程池while (true){//通过 accept 方法,把内核中已经建立好的连接那回到应用程序中Socket clientSocket = serverSocket.accept();/***  此处不能直接调用processConnection,这会导致服务器不能处理多个客户端,通过创建新的线程来调用是更合理的方法*             Thread t = new Thread(()->{*                 try {*                     processConnection(clientSocket);*                 } catch (IOException e) {*                     throw new RuntimeException(e);*                 }*             });*            t.start();*///使用创建线程的方式开销会比较大,此处我们使用线程池的方式service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}// 通过这个方法,来处理当前的连接private void processConnection(Socket clientSocket) throws IOException {//进入方法,先打印一个日志System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());//InputStream 和OutputStream就是在字节流,可以借助这两个对象,完成数据的“发送”和“接收”//通过InputStream 进行read 操作,就是“接收”操作(站在CPU角度)//通过OutputStream 进行 writ额操作,就是“发送”操作。try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//使用try()方式,避免后续使用完了流对象,忘记关闭//由于客户端发来的可能是“多条数据”,针对对条数据,进行循环处理while (true){Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()){//连接断开了,此时循环就应该结束System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());break;}// 1. 读取请求并解析, 此处就以next来作为读取请求的方式,next 的规则:读取到“空白符”就返回。String request = scanner.next();// 2. 根据请求,计算响应String response = process(request);//3. 把响应写回客户端//     方式一:可以把String转成字符数组,写入到OutputStream//      方式二:使用PrintWriter 把OutputStream 包裹一下,来写入字符串PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);  //此处的打印是写入到outputStream 对应的流对象中,也就是写入到 clientSocketprintWriter.flush();  //刷新缓冲区,如果没有此操作,数据仍然可能在内存中,没有被写入网卡// 4.打印一下这次请求交互的内容System.out.printf("[%s:%d] req=%s resp=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}}catch (IOException e){e.printStackTrace();}finally {//在这个地方进行clientSocket的关闭,processConnection就是处理一个连接,如果这个方法执行完毕,这个连接也就处理完了clientSocket.close();}}public String process(String request){// 此处也是写的服务器,响应和请求一样return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}

启动客户端和服务器

运行结果:

//客户端发送请求
TcpEchoClient:-->张三
张三
-->
----------------------------------
//服务器响应
TcpEchoServer:服务器启动!
[/127.0.0.1:60107] 客户端上线
[/127.0.0.1:60107] req=张三 resp=张三

这篇关于JAVAEE值网络编程(2)_TCP流套接字及通信模型、TCP网络编程及代码实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1036095

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法