当前位置: 首页 > news >正文

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接)

文章目录

  • 网络编程
    • TCP流套接字编程
      • ServerSocket API
      • Socket API
      • TCP中的长短连接
      • 手写TCP版本的回显服务器


网络编程

TCP流套接字编程

TCP提供的API主要是两个类:ServerSocket 和 Socket .
TCP不需要一个类来表示"TCP数据报"因为TCP不是以数据报为单位进行传输的.是以字节的方式,流式传输

ServerSocket API

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

ServerSocket 构造方法:

ServerSocket(int port) 创建一个服务端流套接字Socket,并绑定到指定端口.

ServerSocket 方法:

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

在这里插入图片描述

voidclose() 关闭此套接字

Socket API

Socket是既会给客户端使用,也会给服务器使用.

Socket 构造方法:在服务器这边是有accept返回的.在客户端这边,在代码里构造的时候制定一个IP和端口号.(此处的IP和端口是服务器IP和端口)有了这个信息就能和服务器进行连接了.

Socket(String host, intport)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接.
在这里插入图片描述

Socket 方法:

进一步通过Socket对象获取内部的流对象,借助流对象来进行发送/接收.

InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输入流
OutputStream getOutputStream() 返回此套接字的输出流

TCP中的长短连接

在TCP有连接的场景下,针对连接这个概念,有两种典型的表现形式.

  1. 短连接:客户端每次给服务器发消息,先建立连接,发送请求;下次再发送,则重新建立连接.

  2. 长连接:客户端建立连接后,连接先不断开,然后再次发送请求,读取响应;再发送请求,读取响应;若干轮之后,客户端确实短时间之内不再需要使用这个连接了,此时再断开.

两者区别:

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

拓展了解:
在这里插入图片描述

手写TCP版本的回显服务器

在这里插入图片描述

TCP服务端

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

TCP客户端

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.println("> ");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 tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);tcpEchoClient.start();}
}

问题:

当前代码里使用的是println来发送数据,println会在发送的数据后面自动带上\n换行.如果不适用println,而是使用print(不带\n换行) 上面的代码是否能正确运行?

不能正确运行,没有\n是不行的.TCP协议是面向字节流的协议(字节流特性:一次读多少个字节都行).接收方无法知道我们一次要多多少字节,这就需要我们在数据传输中进行明确的约定.此处代码中,隐式约定了使用\n来作为当前代码的请求/响应分割约定.
在这里插入图片描述
但是我们发现上面的代码还是存在一些问题的:
在这里插入图片描述
为解决上面的问题,我们使用多线程,主线程负责进行accept.每次接收到一个连接,创建新线程 ,由这个新的线程负责处理这个新的客户端.每个线程是独立的执行流.每个独立的执行流是各自执行各自的逻辑,彼此之间是并发关系.不会出现一边阻塞而影响到另一边执行的情况.

如果服务器,客户端特别多,很多客户端频繁建立连接就需要频繁创建/销毁线程了, 此时单纯的多线程的处理方法也不行了,所以我们就得用线程池来进行处理.

在这里插入图片描述

如果客户端非常多而且客户端连接都迟迟不断开,就会导致机器上有很多线程.如果一个服务器有几千个客户端就得是几千个线程.这个事情对机器来说,是一个很大的负担.这个时候为了解决单机支持更大量客户端的问题即C10M问题.就想办法让一个线程,处理多个客户端连接.为了解决这个问题操作系统底层提出了IO多路复用,IO多路转接.就是利用充分等待时间,做别的事情.我们给这个线程安排个集合,这个集合就放了一堆连接.这个线程就负责监听这个集合,哪个连接有数据来了,线程就来处理哪个连接,虽然连接有很多,总还是有先有后的.操作系统提供了一些原生API ,select,poll,epoll.在Java中,提供了一组NIO这样类,就封装了上述多路复用的API.

改进后:TCP服务器代码

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("启动服务器!");//此处使用CachedThreadPool ,使用FixedThreadPool不太合适(线程数不太应该是固定的 )ExecutorService threadPool = Executors.newCachedThreadPool();while (true) {//使用这个clientSocket和具体的客户端进行交流Socket clinentSocket = serverSocket.accept();//此处使用多线程处理/*Thread t = new Thread(()->{processConnection(clinentSocket);});*///使用线程池threadPool.submit(()->{processConnection(clinentSocket);});}}//使用这个方法处理一个连接 一个连接对应一个客户端//可能涉及到多次交互private void processConnection(Socket clinentSocket) {//获得客户端IP和端口System.out.printf("[%s:%d] 客户端上线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());// 基于上述socket对象和客户端进行通信try(InputStream inputStream = clinentSocket.getInputStream();OutputStream outputStream = clinentSocket.getOutputStream()){//由于要处理多个请求和响应,也是使用循环while (true){//1. 读取请求Scanner scanner = new Scanner(inputStream);if(!scanner.hasNext()){//没有下一个数据 说明读完了 (客户端关闭了连接)System.out.printf("[%s:%d] 客户端下线!\n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort());break;}//此处使用next是一直读取到换行符/空格/其他空白符结束//但是结果不包含上述空白符String request = scanner.next();//2. 根据请求响应String response = process(request);//3. 返回响应结果//outputStream没有write String这样的功能 可以把String里的字节数组拿出来 进行写入//也可以用字符流来转换一下PrintWriter printWriter = new PrintWriter(outputStream);//此处使用println来写入 让结果中带有一个\n 换行 方便对端来接收解析printWriter.println(response);//flush用来刷新缓冲区 保证当前写入的数据 确实是发送出去了printWriter.flush();System.out.printf("[%s:%d] req: %s; resp: %s \n",clinentSocket.getInetAddress().toString(),clinentSocket.getPort(),request,response);}}catch (IOException e){e.printStackTrace();}finally {try {clinentSocket.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(9090);server.start();}
}

在这里插入图片描述

相关文章:

【网络编程】(TCP流套接字编程 ServerSocket API Socket API 手写TCP版本的回显服务器 TCP中的长短连接)

文章目录 网络编程TCP流套接字编程ServerSocket APISocket APITCP中的长短连接手写TCP版本的回显服务器 网络编程 TCP流套接字编程 TCP提供的API主要是两个类:ServerSocket 和 Socket . TCP不需要一个类来表示"TCP数据报"因为TCP不是以数据报为单位进行传输的.是以…...

企业级PaaS低代码快开平台源码,基于 Salesforce Platform 的开源替代方案

PaaS低代码快开平台是一种快速开发应用系统的工具,用户通过少量代码甚至不写代码就可以快速构建出各种应用系统。 随着信息化技术的发展,企业对信息化开发的需求正在逐渐改变,传统的定制开发已经无法满足企业需求。低代码开发平台&#xff0…...

【LeetCode】72.编辑距离

题目 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作: 插入一个字符删除一个字符替换一个字符 示例 1: 输入:word1 "horse", word2 "…...

大模型,开源干不掉闭源

开源大模型对闭源大模型的冲击,变得非常猛烈。 今年3月,Meta发布了Llama(羊驼),很快成为AI社区内最强大的开源大模型,也是许多模型的基座模型。有人戏称,当前的大模型集群,就是一堆各…...

Redis 九种数据类型的基本操作

一、redis9种数据类型的基本操作 ①key操作 #查找所有的key 127.0.0.1:6379> keys * 1) "pop" 2) "mylist" 3) "lpl" 4) "myset" #设置key的过期时间 返回1表示执行成功,0表示失败,出现问题 127.0.0.1:6379…...

爬取微博热搜榜并进行数据分析

设计方案 爬虫爬取的内容 :爬取微博热搜榜数据。 网络爬虫设计方案概述 用requests库访问页面用get方法获取页面资源,登录页面对页面HTML进行分析,用beautifulsoup库获取并提取自己所需要的信息。再讲数据保存到CSV文件中,进行…...

基于深度神经网络的肺炎检测系统实现

一、说在前面 使用AI进行新冠肺炎图像诊断可以加快病例的诊断速度,提高诊断的准确性,并在大规模筛查中发挥重要作用,从而更好地控制和管理这一流行病。然而,需要强调的是,AI技术仅作为辅助手段,最终的诊断决…...

C# LINQ和Lambda表达式对照

C# LINQ和Lambda表达式对照 1. 基本查询语句 Linq语法: var datafrom a in db.Areas select a ; Lamda语法: var datadb.Areas; sql语法: SELECT * FROM Areas2. 简单的WHERE语句 Linq语法: var datafrom a in db.orderI…...

二、SQL-6.DCL-1).用户管理

一、DCL介绍 Data Control Language 数据控制语言 用来管理数据库 用户、控制数据库的 访问权限。 二、语法 1、管理用户 管理用户在系统数据库mysql中的user表中创建、删除一个用户,需要Host(主机名)和User(用户名&#xff0…...

ElasticSearch学习--数据聚合

介绍 数据聚合可以帮助我们对海量的数据进行统计分析,如果结合kibana,我们还能形成可视化的图形报表。自动补全可以根据用户输入的部分关键字去自动补全和提示。数据同步可以帮助我们解决es和mysql的数据一致性问题。集群可以帮助我们了解结构和不同节点…...

PostMan+Jmeter工具介绍及安装

目录 一、PostMan介绍​编辑 二、下载安装 三、Postman与Jmeter的区别 一、开发语言区别: 二、使用范围区别: 三、使用区别: 四、Jmeter安装 附一个详细的Jmeter按照新手使用教程,感谢作者,亲测有效。 五、Jme…...

AutoSAR系列讲解(实践篇)7.4-实验:配置SWCRTE

注意: 实验篇是重点,有条件的同学最好跟着做一遍,然后回头对照着7.1-7.3理解其配置的目的和意义。实验下篇将在7.7节中继续做 一、实验概览 1、实验目的 通过本次实验,主要是让大家对Dev的配置有一个全流程的学习。这里会用到前两节的内容,将其串联起来,让大家能完整的…...

腾讯云内存型CVM服务器MA3、M6、M6ce和M5处理器CPU说明

腾讯云内存型CVM服务器CPU处理器大全,CVM内存型MA3、内存型M6、安全增强内存型M6ce、内存型M6p、内存型M5、MA2、M4、M3、M2、M1处理器主频、CPU性能性能大全说明,腾讯云内存型云服务器具有大内存的特点,适合高性能数据库、分布式内存缓存等需…...

集睿致远推出CS5466多功能拓展坞方案:支持DP1.4、HDMI2.1视频8K输出

ASL新推出的 CS5466是一款Type-C/DP1.4转HDMI2.1的显示协议转换芯片,,它通过类型C/显示端口链路接收视频和音 频流,并转换为支持TMDS或FRL输出信令。DP接收器支持81.Gbp s链路速率。HDMI输出端口可以作为TMDS或FRL发射机工作。FRL发射机符合HDMI 2.1规范…...

SQL中为何时常见到 where 1=1?

你是否曾在 SELECT 查询中看到过 WHERE 11 条件。我在许多不同的查询和许多 SQL 引擎中都有看过。这条件显然意味着 WHERE TRUE,所以它只是返回与没有 WHERE 子句时相同的查询结果。此外,由于查询优化器几乎肯定会删除它,因此对查询执行时间没…...

React AntDesign表批量操作时的selectedRowKeys回显选中

不知道大家是不是在AntDesign的某一个列表想要做一个批量导出或者操作的时候,发现只要选择下一页,即使选中的ids 都有记录下面,但是就是不回显 后来问了chatGPT,对方的回答是: 在Ant Design的DataTable组件中&#xf…...

anydesk远程控制,主动连接。

目标 远程控制目标电脑,且无需对方同意,并且可以控制目标电脑开关机。 实现 目标电脑和己方电脑均安装anydesk。目标电脑取消开机密码。打开目标电脑的anydesk在设置安全设置中打开为自主访问设置密码。 额外设置 为了让笔记本电脑合盖后仍能被控制…...

Spring Data Redis操作Redis

在Spring Boot项目中&#xff0c;可以使用Spring Data Redis来简化Redis操作&#xff0c;maven的依赖坐标&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></…...

sqlite触发器1

SQLite 的触发器&#xff08;Trigger&#xff09;可以指定在特定的数据库表发生 DELETE、INSERT 或 UPDATE 时触发&#xff0c;或在一个或多个指定表的列发生更新时触发。 SQLite 只支持 FOR EACH ROW 触发器&#xff08;Trigger&#xff09;&#xff0c;没有 FOR EACH STATEM…...

python中——requests爬虫【中文乱码】的3种解决方法

requests是一个较为简单易用的HTTP请求库&#xff0c;是python中编写爬虫程序最基础常用的一个库。 而【中文乱码】问题&#xff0c;是最常遇到的问题&#xff0c;对于初学者来说&#xff0c;是很困恼的。 本文将详细说明&#xff0c;python中使用requests库编写爬虫程序时&…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...