TCP流套接字编程

ops/2024/10/4 17:27:59/

TCP流套接字编程

ServerSocket API

        ServerSocket 是专门给服务器使用的 Socket 对象。

构造方法

方法签名

方法说明

ServerSocket (int port)

创建一个服务端流套接字Socket,并绑定到指定端口

普通方法

方法签名

方法说明

Socket accept()

开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待。

void close()

关闭此套接字

Socket API

        Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。既会给客户端使用,也会给服务器使用。

构造方法

方法签名

方法说明

Socket(String host, int port)

创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

普通方法

方法签名

方法说明

InetAddress getInetAddress()

返回套接字所连接的地址

InputStream getInputStream()

返回此套接字的输入流

OutputStream getOutputStream()

返回此套接字的输出流

         TCP 不需要额外使用一个类来表示“TCP 数据报”,因为 TCP 是以字节的方式流式传输的。 

TCP中的长短连接

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

        短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。

        长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

两者区别如下:

        建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。

        主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。

        两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

扩展了解:

        基于BIO(同步阻塞IO)的长连接会一直占用系统资源。对于并发要求很高的服务端系统来说,这样的消耗是不能承受的。

        由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在一个线程中运行。一次阻塞等待对应着一次请求、响应,不停处理也就是长连接的特性:一直不关闭连接,不停的处理请求。实际应用时,服务端一般是基于NIO(即同步非阻塞IO)来实现长连接,性能可以极大的提升。

示例一:回显服务器

TCP服务端

import java.io.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;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("启动服务器");while (true) {Socket clientSocket = serverSocket.accept(); // 使用这个 clientSocket 和具体的客户端进行交流.processConnection(clientSocket);}}// 使用这个方法来处理一个连接.// 这一个连接对应到一个客户端. 但是这里可能会涉及到多次交互.private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());// 基于上述 socket 对象和客户端进行通信try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 由于要处理多个请求和响应, 也是使用循环来进行.while (true) {// 1. 读取请求Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 没有下个数据, 说明读完了. (客户端关闭了连接)System.out.printf("[%s:%d] 客户端下线! \n", clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}// 注意!! 此处使用 next 是一直读取到换行符/空格/其他空白符结束, 但是最终返回结果里不包含上述 空白符 .String request = scanner.next();// 2. 根据请求构造响应String response = process(request);// 3. 返回响应结果.//    OutputStream 没有 write String 这样的功能. 可以把 String 里的字节数组拿出来, 进行写入;//    outputStream.write(response.getBytes());//    也可以用字符流来转换一下.PrintWriter printWriter = new PrintWriter(outputStream);// 此处使用 println 来写入. 让结果中带有一个 \n 换行. 方便对端来接收解析.printWriter.println(response);// flush 用来刷新缓冲区, 保证当前写入的数据, 雀食是发送出去了.printWriter.flush();System.out.printf("[%s:%d] req: %s; resp: %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),request, response);}} catch (IOException e) {e.printStackTrace();} finally {// 更合适的做法, 是把 close 放到 finally 里面, 保证一定能够执行到!!try {clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(6666);server.start();}
}

        因此在使用完毕之后,就要进行“释放”,UDP 的 socket 没释放是因为 socket 生命周期更长(跟随整个程序),数量也不多;TCP 这里的生命周期更短(这个请求完了就没了),也就是数量也会更多。 

TCP客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {// Socket 构造方法, 能够识别 点分十进制格式的 IP 地址. 比 DatagramPacket 更方便.// new 这个对象的同时, 就会进行 TCP 连接操作.socket = new Socket(serverIp, serverPort);}public void start() {System.out.println("客户端启动!");Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {while (true) {// 1. 先从键盘上读取用户输入的内容System.out.print("> ");String request = scanner.next();if (request.equals("exit")) {System.out.println("goodbye");break;}// 2. 把读到的内容构造成请求, 发送给服务器.PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);// 此处加上 flush 刷新保证数据确实发送出去了.printWriter.flush();// 3. 读取服务器的响应Scanner respScanner = new Scanner(inputStream);String response = respScanner.next();// 4. 把响应内容显示到界面上System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("127.0.0.1", 6666);client.start();}
}

                             相当于打电话的两端,对象是不一样的

示例二:请求响应(短连接)

示例一只是客户端请求和服务端接收,并没有包含服务端的返回响应。以下是对应请求和响应的改造:

构造一个展示服务端本地某个目录(BASE_PATH)的下一级子文件列表的服务

(1)客户端先接收键盘输入,表示要展示的相对路径(相对BASE_PATH的路径)

(2)发送请求:使用客户端Socket的输出流发送TCP报文。即输入的相对路径。

(3)服务端接收并处理请求:使用服务端Socket的输入流来接收请求报文,根据请求的路径,列出下

一级子文件及子文件夹。

TCP服务端

TCP客户端

         

等待客户端建立TCP连接...

目前TCP客户端和服务端实现的功能和UDP差不多,但都存在几个问题:

对于服务端来说,处理一次请求并返回响应后,才能再次处理下一次请求和响应,效率是比较低

的。这个问题比较好解决:可以使用多线程,每次的请求与响应都在线程中处理。这样多个客户端

请求的话,可以在多个线程中并发并行的执行。

服务端解析请求,是只读取了一行,而客户端解析响应,是一直读取到流结束。可以想想为什么解

析请求时,没有读取到流结束?目前的业务,双方都已约定好业务是展示目录下的文件列表,且都只需要一种数据:请求传输的数

据代表要展示的目录;响应传输的数据代表文件列表:每一行为一个文件名。

如要提供更多的业务,如文件重命名,文件删除等操作时,就不能了。此时就需要提供更多的字段

来标识。一般我们需要设计更强大的协议。


http://www.ppmy.cn/ops/20665.html

相关文章

PotatoPie 4.0 实验教程(21) —— FPGA实现摄像头图像二值化(RGB2Gray2Bin)

PotatoPie 4.0开发板教程目录(2024/04/21) 为什么要进行图像的二值化? 当我们处理图像时,常常需要将其转换为二值图像。这是因为在很多应用中,我们只对图像中的某些特定部分感兴趣,而不需要考虑所有像素的…

HarmonyOS NEXT应用开发性能优化从入门到精通指南~

概述 在开发HarmonyOS NEXT应用时,优化应用性能是至关重要的。本文将介绍应用开发过程中常见的一些性能问题,并提供相应的解决方案,配合相关参考示例,帮助开发者解决大部分性能问题。 我们把应用性能分析的方法划分为了性能分析…

前端工程化Vue使用Node.js设置国内高速npm镜像源(踩坑记录版)

前端工程化Vue使用Node.js设置国内高速npm镜像源(踩坑记录版) 此篇仅为踩坑记录,并未成功更换高速镜像源,实际解决方法见文末跳转链接。 1.自身源镜像 自身镜像源创建Vue项目下载速度感人 2.更改镜像源 2.1 通过命令行配置 前提…

磁性呼吸传感技术与机器学习结合在COVID-19审断中的应用

介绍 呼吸不仅是人类生存的基础,而且其模式也是评估个体健康状态的关键指标。异常的呼吸模式往往是呼吸系统疾病的一个警示信号,包括但不限于慢性阻塞性肺病(COPD)、阻塞性睡眠呼吸暂停(OSA)、肺炎、囊性纤…

基于ssm + 小程序的党建考试系统实现与设计(源码+数据库+文档)

基于ssm 小程序的“党建考试系统”实现与设计(源码数据库文档) 开发语言:Java数据库:MySQL技术:SpringmvcSpringMybatis、vue、小程序工具:IDEA/Ecilpse、Navicat、Maven 系统展示 管理系统-登录界面展示 管理系统…

Jenkins - macOS 上安装

文章目录 关于 JenkinsmacOS 上安装 Jenkins方式一:brew方式二:tomcat Jenkins war 关于 Jenkins 官网上下载Jenkins并将其安装到持续集成服务器 https://jenkins.io/download/ macOS 上安装 Jenkins 现在本 macOS 上测试 https://www.jenkins.io/do…

db2 export

DB2的EXPORT命令用于将数据从数据库表导出到外部文件。以下是EXPORT命令的基本语法和一些常见的用法&#xff1a; ### 基本语法&#xff1a; sql EXPORT TO <filename> OF <filetype> LOBS TO <lob-path> LOBFILE <lob-filename> XML TO <xml-path…

贪心总结导航【左程云算法】

贪心总结导航【左程云算法】 前言版权贪心总结导航最后 前言 2024-4-23 14:05:23 以下内容源自《【左程云算法】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://jsss-1.blog.csdn.net 禁止其他平台…