网络编程套接字之TCP

news2024/11/18 3:45:17

在这里插入图片描述

文章目录

  • 一、TCP流套接字编程
    • ServerSocket
    • Socket
    • TCP长短连接
  • 二、TCP回显服务器客户端
    • 服务器
    • 客户端
    • 并发服务器
    • UDP与TCP

一、TCP流套接字编程

我们来一起学习一下TCP socket api的使用,这个api与我们之前学习的IO流操作紧密相关,如果对IO流还不太熟悉的,可以看看这篇IO流操作

ServerSocket

顾名思义,ServerSocket是创建TCP服务器的Socket对象

构造方法作用
ServerSocket(int port)创建一个服务器套接字Socket,并指定端口号
方法作用
Socket accept()开始监听指定端口,有客户端连接时,返回一个服务端Socket对象,并基于该Socket对象与客户端建立连接,否则阻塞等待
void close()关闭此套接字

Socket

我们这里的Socket既是客户端的Socket,也可能是服务器接收到客户端连接后,返回的服务器Socket,不论是那个Socket,都是双方建立连接后,保存对方信息,进行收发数据的。

构造方法作用
Socket(String host,int port)创建一个客户端Socket对象,并与对应IP主机,对应端口的进程进行连接
方法作用
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回套接字的输入流
OutputStream getOutputStream()返回套接字的输出流

TCP长短连接

顾名思义,我们的TCP的长短连接,就表示我们TCP建立连接后,什么时候关闭连接就决定了是长连接还是短链接。
短连接: 在每次接收到数据并返回响应后,关闭连接。也就是说短连接只能收发一次数据。
长连接: 一直保持连接的状态,不关闭连接,双方可以不停的收发数据。

两者各有优缺,短连接适用于客户端请求频率不高的场景,浏览网页等。长连接适用于客户端与服务器频繁通信的场景,视频通话等。

二、TCP回显服务器客户端

服务器

有了昨天UDP实现的基础,今天我们的TCP实现已经有些部分就比较容易理解了。

public class EchoServer {
    private ServerSocket serverSocket = null;
    
    public EchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
}

先创建TCP服务器Socket对象,并指定端口号。

Socket clientSocket = serverSocket.accept();

与客户端进行连接,如果没有获取到连接,则会发生阻塞等待,每获取到一个客户端连接就会返回一个Socket对象,该Socket对象是专门负责与连接的客户端进行通信的。
我们获取到的每一个Socket对象,可能会进行多次通信,所以我们获取到连接后,将通信封装成一个方法。

private void processCoonection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try{
                clientSocket.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

我们先获取与输出输入流,因为需要释放我们直接将它们放到try()中,然后在finally里释放Socket对象资源。

Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    //数据已经读完了
                    System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                String request = scanner.next();

我们将输入流封装到Scanner里,从控制台输入,然后判断控制台是否还有数据输入,如果没有数据输入就退出,然后获取客户端的请求。

/直接返回客户端的请求
                String response = process(request);
                //将输出流封装成打印流
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s;resp: %s\n",clientSocket.getInetAddress().toString(), clientSocket.getPort(),
                        request, response);

在这里插入图片描述

public class EchoServer {
    private ServerSocket serverSocket = null;

    public EchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            Socket clientSocket = serverSocket.accept();
            processCoonection(clientSocket);
        }
    }

    private void processCoonection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            while (true) {
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    //数据已经读完了
                    System.out.printf("[%s:%d] 客户端下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                String request = scanner.next();
                //直接返回客户端的请求
                String response = process(request);
                //将输出流封装成打印流
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                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 {
            try{
                clientSocket.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        return request;
    }
}

我们现在实现的这个TCP server有个致命的缺点,一次只能处理一个客户端,等我们写完客户端再来分析。

客户端

public class EchoClient {
    private Socket socket = null;
    public EchoClient(String serverIp,int serverPort) throws IOException {
        //我们TCP的Socket对象能够识别点分十进制的IP
        //我们在创建对象的时候,就会与服务器进行连接
        socket = new Socket(serverIp,serverPort);
    }
}

我们在new 这个对象的过程,就会触发TCP建立连接的过程,如果我们客户端没有这部分代码,那么服务器就会在accept进行阻塞等待。

public void start() {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while (true) {
                System.out.print("> ");
                String request = scanner.next();
                if(request.equals("exit")) {
                    System.out.println("客户端退出!");
                    break;
                }
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.flush();
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                System.out.println(response);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

我们客户端的收发数据与服务器大差不差,这里就不在一一解释了。
在这里插入图片描述
在这里插入图片描述
分别启动客户端服务器程序。

在这里插入图片描述
在这里插入图片描述
可以成功的收发数据,但是我们当前代码有一个很严重的问题,服务器同一时刻只能处理一个连接,这样是很鸡肋的,我们对服务器进行更新。

并发服务器

我们看观察一下我们服务器的代码。
在这里插入图片描述
当我们客户端连接上这个服务器的时候,就执行到processConnection方法的while循环中,只要该方法不结束,我们的accpet就无法获取到第二个客户端socket对象。
那么我们如何解决这个问题呢?
使用多线程,我们的主线程专门负责进行accept,每收到一个连接,创建新线程,由新线程来负责处理这个新的客户端。

public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            Socket clientSocket = serverSocket.accept();
            Thread t = new Thread(() -> {
                processCoonection(clientSocket);
            });
            t.start();
        }
    }

我们可以使用多线程来解决这个问题,但是现在每获取到一个连接就会创建一个线程,如果同一时刻连接过多,我们创建了大量线程,资源全部耗费在了线程切换上面了,我们可以使用线程池来提升效率。

public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) {
            Socket clientSocket = serverSocket.accept();
            threadPool.submit(() -> {
                processCoonection(clientSocket);
            });
        }
    }

尽管我们使用了线程池了,但还是不够,如果我们的客户端非常多,而且都迟迟不断开,就会导致我们会有很多线程,对于我们来说是一个很大的负担。
能否有办法解决单机支持更大量客户端的问题呢?也是经典的C10M(单机处理1KW个客户端)问题
这里并不是说单机真正能处理1KW个客户端,只是表达说客户端的量非常大,针对我们上述多线程的版本,我们的机器是承受不了这么多线程的开销的,那么是否有办法一个线程处理很多客户端连接呢? 这就是IO多路复用,IO多路转接技术
给线程安排一个集合,这个集合放了一堆连接,我们线程负责监听集合,那个连接有数据来了,就处理那个连接。虽然我们的连接有很多,但是我们这里的连接并不是严格意义上的同时,也是有先后的,我们的操作系统里,提供了一些API,比如select,poll,epoll,我们的java里,也提供了一组NIO这样的类,封装了上述技术。

UDP与TCP

我们学习了TCP与UDP的网络编程后,来进行一个对比。
TCP:
在这里插入图片描述
UDP:
在这里插入图片描述

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

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

相关文章

Springboot 我随手封装了一个万能的导出excel工具,传什么都能导出

前言 如题,这个小玩意,就是不限制你查的是哪张表,用的是什么类。 我直接一把梭,嘎嘎给你一顿导出。 我知道,这是很多人都想过的, 至少我就收到很多人问过我这个类似的问题。 我也跟他们说了,但…

python冒号的用法总结

一维数组 1. 单个冒号的情况 1.1 写完整的情况下 单个冒号的情况下,对数组的遍历操作是从前向后操作。如:arr[a:b] ,冒号前的a含义是从a开始遍历,冒号后的b含义是到b截止(不包括b)。 arr [1, 2, 3, 4,…

Qt扫盲-QMake 语言概述

QMake 语言概述一、概述二、变量三、替换函数四、测试函数一、概述 这里主要就是记录一下如何使用 qmake Manual,里面关于我对 qmake的理解,以及如何配置这个 qt 工程文件,通过配置工程文件,来构建出,APP,…

如何在Github上配置ssh key的密钥

Step0 : 解释说明 git使用SSH配置, 初始需要以下三个步骤 使用秘钥生成工具生成rsa秘钥和公钥将rsa公钥添加到代码托管平台将rsa秘钥添加到ssh-agent中,为ssh client指定使用的秘钥文件 Step 1: 核验本地主机是否已经存在ssh密钥。(若id_rs…

ChatGPT之文章生成

文章目录介绍激励设计Prompt Design故障调整Troubleshooting分类Classification提高分类器的效率生成对话内容转化翻译转化总结实际问答插入文本编辑模式介绍 ChatGPT功能很强大,它可以根据你给出的模板和文本进行文本补全,最好的探索方式就是使用我们给…

数据预处理(无量纲化、缺失值、分类特征、连续特征)

文章目录1. 无量纲化1.1 sklearn.preprocessing.MinMaxScaler1.2 sklearn.preprocessing.StandardScaler2. 缺失值3. 分类型特征4. 连续型特征数据挖掘的五大流程包括:获取数据数据预处理特征工程建模上线 其中,数据预处理中常用的方法包括数据标准化和归…

3分钟学会图新地球图源制作详细教程

图新地球图源制作 1.资源准备 (1) 准备一份图新地球支持的lrc格式的图源; (2) 安装图新地球 (LSV)软件和奥维omap软件 。 2.操作步骤 (1) 用记事本打开lrc格式的文件(图①),同时用奥维软件…

Unity 多语言 轻量高效的多语言工具集 LanguageManager

效果展示 支持excel导入自动化 组件化 更方便 也提供直接获取多语言的接口 没有挂 LanguageText的对象也可以获取多语言文本内容 支持 Format接口 可以传递N个参数进来组装多语言 支持首次系统语言自测 支持语言切换后本地自动保存配置 支持实时切换 同步刷新所有UI 容错处…

Maven:在Intellij idea的使用

MavenIntellij idea配置MavenIntellij idea创建Maven项目Java项目的创建Web项目的创建Intellij idea执行Maven命令JavaWeb项目转为Maven项目遇到的问题解决了解到Maven的基础知识后,接下来,简单介绍Maven在Intellij idea的使用( 以 IntelliJ …

在线开会,来开开圆桌会议吧~

圆桌会议应用场景:适合内部培训、部门会议亦或是头脑风暴等较为轻松的场景,有兴趣的朋友可以联系我来测试哦~~ 上图: 图:圆桌会议应用截图 在圆桌布局之下,企业可以将每一位参会者和座位绑定,1:1模拟线下圆…

微信中如何接入机器人才比较安全(不会收到警告或者f号)之第三步正式接入

大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。 前言 前面两篇文章分别介绍了下chatgpt接入方式: 微信中如何接入chatgpt机器人才比较安全(不会收到警告或者f号)之第一步登录微信 微信中如何接入chatgpt机器人才比较安全(不会收到警告或者f号)之第二步注入dll文件…

Spring 底层原理与解析 - 容器接口

Spring 底层原理与解析 - 容器接口 BeanFactory 能做哪些事 BeanFactory 与 ApplicaiotnContext 到底是谁提前做完了对象的加载 在之前的一篇关于 Spring 的文章Spring IoC 与容器的初始化中提到过,BeanFactory 接口与 ApplicationContext 接口之间的关系 可以看…

MySQL作业四

学生表:Student (Sno, Sname, Ssex , Sage, Sdept) 学号,姓名,性别,年龄,所在系 Sno为主键 课程表:Course (Cno, Cname,) 课程号,课程名 Cno为主键 学生选课表:SC (Sno, Cno, Score)…

编译链接实战(8)认识elf文件格式

🎀 关于博主👇🏻👇🏻👇🏻 🥇 作者简介: 热衷于知识探索和分享的技术博主。 💂 csdn主页::【奇妙之二进制】 ✍️ 微信公众号:【Linux …

FreeRTOS的列表和列表项

目录 列表和列表项的简介 列表和列表项的关系 列表相关API函数介绍 函数vListInitialiseI() 函数vListInitialiseItem() 函数vListInsert() 函数vListInsertEnd() 函数uxListRemove() 列表和列表项的简介 列表是FreeRTOS中的一个数据结构,概念上和链表有点类…

电脑的安全模式安全吗?如何进入安全模式?

“怎么进安全模式?”这条留言成功引起了驱动哥的注意。 在遇到电脑蓝屏、黑屏等系统问题的时候,如何在更安全且不损失电脑文件的情况下修复故障?系统早已为大家准备好了相应的修复策略。 没错!电脑也有属于自己的Plan B——安全…

我工作5年测试才8K,应届生刚毕业就拿16K?凭什么

我从事手工测试五年了,还拿着8K的死工资,家里还几张嘴需要喂养,我很累,也很迷茫…【某个粉丝跟我的诉说】 为什么手工测试会迷茫呢? 自动化测试、性能测试倒是不会迷茫。 我认为手工测试的迷茫基于两个原因&#xf…

数据分析与SAS学习笔记5

DATALINES语句: 相当于CARS语句; 该语句必须是数据步的最后一条语句; MISSOVER处理; DATA TEMP; INFILE DATALINES MISSOVER; INPUT X Y Z; DATALINES; 1 10 100 2 20 3 30 300 ; PROC PRINT; RUN; 代码说明: 1&am…

生物信息场景下的用户需求

背景分析概念定义基因测序是一种新型基因检测技术,是基因检测的方法之一,其又叫基因谱测序,是国际上公认的一种基因检测标准。基因测序技术能锁定病变基因,提前预防和治疗。过长的测序周期以及上万美元的仪器成本,成了…

第二章:unity性能优化之drawcall优化-1

目录 前言: 一、什么是drawcall 二、如何合批 1、什么是合批? 2、静态批处理 1、什么是静态批处理: 2、静态合批的规则 3、动态批处理 4、GPU Instancing 1、GPU instancing的定义 2、编写支持GPU instancing Shader步骤 5、…