【网络编程】(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();}
}

在这里插入图片描述

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

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

相关文章

管理类联考——数学——趣味篇——可视化

Manim: 一个数学可视化的动画引擎 官网:https://3b1b.github.io/manim/index.html 名词解析 python3.7是python语言的解释器, 运行python程序的环境必备品. 这个没啥说的,大家都能懂. 虽然官方建议3.7,但是我用3.8发现也没问题.考虑未来的历史进程,大伙最好还是装…

html中使用Vue+element UI动态创建表单数据不显示问题

直接上代码&#xff1a;html代码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&…

Ansible的应用

Ansible简介 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。 Ansible能批量配置、部署、管理上千台主机…

Io进、线程——进程的基础

进程的基础 进程是计算机中最基本的执行单位&#xff0c;是程序在操作系统中的一次执行过程。每个进程都有自己的地址空间、数据栈、程序计数器等&#xff0c;相互之间独立运行&#xff0c;互不干扰。进程间的通信通过特定的机制来实现&#xff0c;进程的创建和撤销由操作系统…

[AWD靶场搭建]

文章目录 [AWD靶场搭建]前言AWD平台搭建靶机搭建Cadinal添加靶机 连接Asteroid大屏默认ssh账号密码参考 [AWD靶场搭建] 前言 觉得好玩搭建了一下AWD靶场&#xff0c;使用了vidar-team编写的 Cardinal AWD平台搭建 这里我是在kali搭建的&#xff0c;所以我下载了这个压缩包&…

FANUC机器人SRVO-050碰撞检测报警和SRVO-053干扰值过大故障报警总结

FANUC机器人SRVO-050碰撞检测报警和SRVO-053干扰值过大故障报警总结 前面和大家分享了关于SRVO-050碰撞检测报警和SRVO-053干扰值过大的原因分析以及处理方法,感兴趣的朋友可以参考以下链接中的内容: FANUC机器人SRVO-050碰撞检测报警原因分析及处理对策

Java阶段五Day15

Java阶段五Day15 文章目录 Java阶段五Day15分层其他依赖dao-apidao-implinfrustructuredomainadaptermain 测试整合项目main前台师傅功能luban-front配置师傅相关表格ER图ER练习案例鲁班表格ER关系&#xff08;非常重要&#xff09; 前台师傅接口——师傅入驻adapterdomaininfr…

如何判断某个视频是深度伪造的?

目录 一、前言 二、仔细检查面部动作 三、声音可以提供线索 四、观察视频中人物的身体姿势 五、小心无意义的词语 深造伪造危险吗&#xff1f; 一、前言 制作深度伪造视频就像在Word文档中编辑文本一样简单。换句话说&#xff0c;您可以拍下任何人的视频&#xff0c;让他…

谷粒商城第六天-实现功能的前序工作(网关的配置 跨域配置)

目录 一、为什么要做这项工作 1.1 为什么要配置网关 1.2 为什么要使用网关统一配置跨域 二、网关配置 三、统一跨域配置 四、总结 一、为什么要做这项工作 1.1 为什么要配置网关 我们知道网关的作用其实主要就是进行路由的&#xff0c;也就是根据前端发送到网关的请求&…

【docker】docker部署nginx

目录 一、步骤二、示例 一、步骤 1.搜索nginx镜像 2.拉取nginx镜像 3.创建容器 4.测试nginx 二、示例 1.搜索nginx镜像 docker search nginx2.拉取nginx镜像 docker pull nginx3.创建容器&#xff0c;设置端口映射、目录映射 # 在root目录下创建nginx目录用于存储nginx数据…

Vue3解决:Mockjs 引入后并访问 404(Not Found) 的页面报错问题

1、问题描述&#xff1a; 其一、报错为&#xff1a; GET http://localhost:5173/list 404 (Not Found) ncaught (in promise) AxiosError {message: Request failed with status code 404, name: AxiosError, code: ERR_BAD_REQUEST, config: {…}, request: XMLHttpRequest,…

大采购3.0,打造企业采购数智化的韧性变革!

大采购3.0&#xff0c;深化采购全链路管控&#xff0c;聚焦数智化运营、智慧化监管、个性化需求适配、一体化协同、稳定可靠、安全可信、企业级服务七大核心基因&#xff0c;围绕采购管理、供应商全生命周期管理、人工智能深入应用、易用性、交付能力等方面进行了全面升级和优化…

.NET 8 Preview 5推出!

作者&#xff1a;Jiachen Jiang 排版&#xff1a;Alan Wang 我们很高兴与您分享 .NET 8 Preview 5 中的所有新功能和改进&#xff01;此版本是 Preview 4 版本的后续版本。在每月发布的版本中&#xff0c;您将看到更多新功能。.NET 6 和 7 用户可以密切关注此版本&#xff0c;而…

数据结构--图的遍历 BFS

数据结构–图的遍历 BFS 树的广度优先遍历 从 1 结点进行 b f s bfs bfs的顺序&#xff1a; 【1】 【2】【3】【4】 【4】【6】【7】【8】 图的广度优先遍历 从 2 号点开始 b f s bfs bfs的顺序&#xff1a; 【2】 【1】【6】 【5】【3】【7】 【4】【8】 树 vs 图 不存在“回…

系统学习Linux-MySQL服务基础(一)

一、MySQL服务概述 什么是数据库&#xff1f; 将大量数据保存起来&#xff0c;通过计算机加工而成的可以进行高效访问的数据集合 数据库是存储、管理和操作组织化数据的软件系统 数据库能干什么&#xff1f; 企业应用存放用户数据、管理企业数据金融行业存储分析客户的财务…

pear文件利用 (远程文件下载、生成配置文件、写配置文件) 从一道题看——CTFshow私教 web40

web40 考点&#xff1a;pear文件包含 pear是PHP的一个扩展 条件&#xff1a; 1 有文件包含点 2 开启了pear扩展 &#xff08;可以当他是一个框架&#xff09; 3 配置文件中register_argc_argv 设置为On,而默认为Off&#xff08;$_SERVER[‘argv’]生效&#xff09; 4 找到…

【力扣每日一题】2023.7.25 将数组和减半的最少操作次数

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个数组&#xff0c;我们每次可以将任意一个元素减半&#xff0c;问我们操作几次之后才可以将整个数组的和减半&…

【cpolar内网穿透工具】

文章目录 cpolar内网穿透工具.md概述什么是cpolar&#xff1f;cpolar可以用在哪些场景&#xff1f; 1. 注册cpolar帐号1.1 访问官网站点&#xff1a;[https://www.cpolar.com](https://link.zhihu.com/?targethttps%3A//www.cpolar.com/) 1.2 注册帐号 2. 下载Windows版本cpol…

【ARM Cache 系列文章 2 -- Cache Coherence及内存顺序模学习】

文章目录 Cache Coherence 背景1.1 内存顺序模型简介(Memory Model)1.1.1 Normal Memory1.1.2 Device Memory 1.2 Cache 一致性问题解决方案1.2.1 Shareability 属性1.2.2 Non-Shareable 属性1.2.3 Inner-Shareable 属性1.2.4 Out-Shareable 属性 1.3 Shareability 和 PoC/PoU …

【干货防踩坑/全图文分步/Gitlab镜像仓库自动同步】Gitlab CE/EE镜像仓库的配置技巧(含ssh/密码两验证方法)

【干货踩坑】Gitlab CE/EE镜像仓库的配置技巧&#xff08;含ssh/密码两验证方法&#xff09; 众所周知&#xff0c;Gitlab是个好东西。为什么呢&#xff1f;GitHub没有的功能&#xff0c;他全有了。更何况还可以私有部署。这两天自己部署了Gitlab&#xff0c;然后想把自己的项…