javaEE-网络编程4.TCP回显服务器

news2025/2/28 15:39:19

目录

TCP流套接字编程

一.API介绍

ServerSocket类

构造方法:

​编辑方法:

Socket类

构造方法:

方法:

二、TCP连接

三、通过TCP实现回显服务器

TCP服务端:

1.创建Socket对象

2.构造方法

3.start方法

TCP客户端:

1.创建Socket对象

2.构造方法:

3.start方法

服务端参考代码:

客户端参考代码:

参考结果:


TCP流套接字编程

一.API介绍

有两个关键的类:

ServerSocket:是创建TCP服务端Socket的API。给服务器使用的类,使用这个类,绑定端口号.

(马路牙子上的拉客销售)

Socket:既会给客户端使用,又会给服务器使用。是客户端的Socket,又是服务器接收到客户端accept请求后,返回的服务端Socket.

(售楼处的销售顾问)

ServerSocket类

构造方法:

方法:

Socket类

既会给客户端使用,又会给服务器使用

作用:双方建立连接后,保存对端信息,用来与对方收发数据。

构造方法:

方法:

二、TCP连接

对于UDP来说,是无连接的,每次发送数据时,都需要手动在send方法中指定目标地址;

但是TCP是有连接的,需要提前把连接功能建立起来,

连接如何建立,不需要代码干预,是系统内核自动负责完成的。

我们要做的就是:客户端:发起“建立连接”的动作;

                                服务器:把建立好的连接从内核中拿到应用程序里。

三、通过TCP实现回显服务器

TCP服务端

1.创建Socket对象

2.构造方法

3.start方法

要循环的读取客户端的连接.因此要在循环中

任务1:接听连接,通过调用accept方法

任务2:建立连接,通过实现processConnection方法,建立连接

修改1:将连接改成多线程模式

当多个客户端同时发出请求时,由于单线程,这能处理一个客户端请求,将连接改成多线程,同时获取多用户的请求;但是还有一个问题,每来一个客户端,就创建一个线程,客户端结束,就销毁这个线程,当多个客户端发起请求时,就要创建和销毁大量的线程,开销就会较大;

可以用线程池的方法来创建多线程,提前将线程创建好,需要时就直接用,不用时就先放着,不需要创建和销毁。

线程池解决了线程频繁创建和销毁的开销,但是当多个线程被创建,而没有被销毁,就会导致当前服务器上积累了的大量的线程,这对于服务器的负担会非常严重。

为了解决这个问题,还可以引入其他方案:

1、引入协程。(轻量级线程)本质上还是线程,用户可以通过手动调度的方法,让一个线程并发的执行多个任务。(解决了线程调度的开销)

2.IO多路复用。(系统内核级别的机制)让一个线程负责处理多个Socket对象,这里需要Socket数据不是同时需要被处理的。

4.processConnection方法

输出客户端IP和端口号,表示客户端上线

通过调用Socket的getInputStream方法获取客户端请求,和getOutputStream返回响应

任务2.1 读取客户端请求

读取结束,则表明客户端下线,显示客户端IP和端口号

    2.2,请求并解析 注意:next读取到空白字符才结束

       

    2.3根据请求解析响应

   

这里的process方法建议写成public,方便之后子类通过继承TcpEchoServer方法,实现特有的功能

2.4把响应返回给客户端, 通过PrintfWriter的println方法返回.

修改2:手动刷新缓冲区

这里的println会有缓冲区,当读取到客户的请求时,不会计算响应,而是等缓冲区满了,才一起处理,因此,每次请求,要手动刷新缓冲区,才能保证客户端的每次请求都能及时响应

TCP客户端:

1.创建Socket对象

2.构造方法:

3.start方法

因为TCP是以字节流的形式发送请求的,要用InputStream来获取用户的输入; 获取到服务端的响应,也要用OutputStream来接收

任务1:获取用户输入请求

2.将请求发送给服务端 通过PrintWriter的println方法发送,这样可以获取到y用户输入的 "\n"

修改1:手动刷新缓冲区

客户端这里的println也有缓冲区的问题,每次输入后,也要手动刷新,将请求发送给服务端

3.从服务端获取响应 通过Scanner读取OutPutStream的输出流来获取服务器返回的响应

4.显示响应

修改2:对Socket手动关闭

目前这个代码会出现文件资源泄露,创建的Socket,没有进行close(),

这里的关闭,只是关闭了输出流和输入流,并没有关闭Socket

因此,为了避免文件资源泄露,当客户端断来连接后,要在客户端的finally代码块中手动关闭

服务端参考代码:


class TcpEchoServer1{
    //创建ServerSocket对象
    private ServerSocket serverSocket=null;
    //提供客户端端口号
    public TcpEchoServer1(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务端启动");
        ExecutorService pool = Executors.newCachedThreadPool();
        while(true){
            //1.通过accpet方法 接听连接
            Socket clientSocket = serverSocket.accept();
            //2.通过实现processConnection方法 建立连接
//            processConnection(clientSocket);
            pool.submit(new Runnable(){//通过线程池,创建多条线程
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }
    //TCP是以字节流的方式发送请求的,要用InputStream来读取发送的请求,
                        //    用OutputStream来 返回响应
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s,%d] 客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            //循环读取客户端的请求,并返回响应
           while(true){
               //1.读取请求
               Scanner scan=new Scanner(inputStream);
               if(!scan.hasNext()){
                   //读取完毕,客户端断开连接,产生 读取完毕
                   System.out.printf("[%s,%d] 客户端下线\n",clientSocket.getInetAddress(),clientSocket.getPort());
                   break;
               }
               //2,请求并解析 注意:next读取到空白字符才结束
               String request=scan.next();
               //3.根据请求,计算响应
               String response=process(request);
               //4.把响应返回给客户端
               //给outputStream套上一层,可以获取 \n 更方便操作
               PrintWriter printWriter=new PrintWriter(outputStream);
               printWriter.println(response);
               printWriter.flush();//手动刷新缓冲区
               System.out.printf("[%s,%d] req:%s, resp:%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),
                                    request,response);

           }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String process(String request) {
        //这里还是什么都不做,就返回请求
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer1 tcpEchoServer1 = new TcpEchoServer1(9090);
        tcpEchoServer1.start();
    }
}

客户端参考代码:


class TcpEchoClient1{
    //1.创建Socket对象
    private Socket clientSocket=null;
    //传入服务端的IP和端口号
    public TcpEchoClient1(String serverIp,int serverPort) throws IOException {
        clientSocket=new Socket(serverIp,serverPort);
    }
    public void start() throws IOException {
        System.out.println("客户端启动");
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream();
            Scanner scanNetWork=new Scanner(inputStream);
            Scanner scanConsole=new Scanner(System.in);
            PrintWriter printWriter=new PrintWriter(outputStream) ) {

            while(true){
                System.out.print("->");
                if(!scanConsole.hasNext()){
                    break;//客户端请求输入结束
                }
                //1.获取请求
                String request=scanConsole.next();
                //2.将请求发送给服务端 通过PrintWriter的println方法发送
                printWriter.println(request);
                printWriter.flush();
                //3.获取响应
                String response=scanNetWork.next();
                //4.输出响应
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }
    }
    public static void main(String[] args) throws IOException {
        TcpEchoClient1 tcpEchoClient1 = new TcpEchoClient1("127.0.0.1", 9090);
        tcpEchoClient1.start();
    }
}

参考结果:

可以同时启动多个客户端,与同一个服务端建立连接

 

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

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

相关文章

RIS智能无线电反射面:原理、应用与MATLAB代码示例

一、引言 随着无线通信技术的快速发展,人们对通信系统的容量、覆盖范围、能效以及安全性等方面的要求日益提高。传统的无线通信系统主要通过增加基站数量、提高发射功率和优化天线阵列等方式来提升性能,但这些方法面临着资源有限、能耗高和成本上升等挑战。因此,探索新的无线…

合并模型带来的更好性能

研究背景与问题提出 在人工智能领域,当需要处理多个不同任务时,有多种方式来运用模型资源。其中,合并多个微调模型是一种成本效益相对较高的做法,相较于托管多个专门针对不同任务设计的模型,能节省一定成本。然而&…

城市生命线安全综合监管平台

【落地产品,有需要可留言联系,支持项目合作或源码合作】 一、建设背景 以关于城市安全的重要论述为建设纲要,聚焦城市安全重点领域,围绕燃气爆炸、城市内涝、地下管线交互风险、第三方施工破坏、供水爆管、桥梁坍塌、道路塌陷七…

Flink系列知识讲解之:网络监控、指标与反压

Flink系列知识之:网络监控、指标与反压 在上一篇博文中,我们介绍了 Flink 网络协议栈从高层抽象到底层细节的工作原理。本篇博文是网络协议栈系列博文中的第二篇,在此基础上,我们将讨论如何监控网络相关指标,以识别吞…

生物医学信号处理--随机信号的数字特征

前言 概率密度函数完整地表现了随机变量和随机过程的统计性质。但是信号经处理后再求其概率密度函数往往较难,而且往往也并不需要完整地了解随机变量或过程的全部统计性质只要了解其某些特定方面即可。这时就可以引用几个数值来表示该变量或过程在这几方面的特征。…

计算机网络 (31)运输层协议概念

一、概述 从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。运输层的一个核心功能是提供从源端主机到目的端主机的可靠的、与实际使用的网络无关的信息传输。它向高层用…

深度学习张量的秩、轴和形状

深度学习张量的秩、轴和形状 秩、轴和形状是在深度学习中我们最关心的张量属性。 秩轴形状 秩、轴和形状是在深度学习中开始使用张量时我们最关心的三个属性。这些概念相互建立,从秩开始,然后是轴,最后构建到形状,所以请注意这…

积分与签到设计

积分 在交互系统中,可以通过看视频、发评论、点赞、签到等操作获取积分,获取的积分又可以参与排行榜、兑换优惠券等,提高用户使用系统的积极性,实现引流。这些功能在很多项目中都很常见,关于功能的实现我的思路如下。 …

vue实现虚拟列表滚动

<template> <div class"cont"> //box 视图区域Y轴滚动 滚动的是box盒子 滚动条显示的也是因为box<div class"box">//itemBox。 一个空白的盒子 计算高度为所有数据的高度 固定每一条数据高度为50px<div class"itemBox" :st…

IEC61850遥控-增强安全选控是什么?

摘要&#xff1a;遥控服务是IEC61850协议中非常重要的一项服务&#xff0c;其通常会被应用在电源开关、指示灯、档位调节等器件的操作。 遥控是一类比较特殊的操作&#xff0c;其通过远程方式操作指定的设备器件&#xff0c;在一些重要的场景中需要有严谨的机制来进行约束&…

【Uniapp-Vue3】创建自定义页面模板

大多数情况下我们都使用的是默认模板&#xff0c;但是默认模板是Vue2格式的&#xff0c;如果我们想要定义一个Vue3模板的页面就需要自定义。 一、我们先复制下面的模板代码&#xff08;可根据自身需要进行修改&#xff09;&#xff1a; <template><view class"…

如何操作github,gitee,gitcode三个git平台建立镜像仓库机制,这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈

如何操作github&#xff0c;gitee&#xff0c;gitcode三个git平台建立镜像仓库机制&#xff0c;这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈 问题背景 由于我司最早期19年使用的是gitee&#xff0c;因此大部分仓库都在gitee有几百个库的代码&#xff0c;…

QThread多线程详解

本文结构如下 文章目录 本文结构如下 1.概述2.开始多线程之旅2.1应该把耗时代码放在哪里&#xff1f;2.2再谈moveToThread() 3.启动线程前的准备工作3.1开多少个线程比较合适&#xff1f;3.2设置栈大小 4.启动线程/退出线程4.1启动线程4.2优雅的退出线程 5.操作运行中的线程5.1…

深度学习数据集有没有规范或指导意见,数据集的建立都需要做哪些研究工作?

一、数据集的核心原则是什么&#xff1f; 数据集的目标&#xff1a;它需要回答“你要解决什么问题&#xff1f;” 在构建数据集之前&#xff0c;最重要的不是去采集数据&#xff0c;而是明确目标&#xff1a; 你的模型是要做图像分类&#xff0c;还是目标检测&#xff1f;是要…

前端for循环遍历——foreach、map使用

title: 前端不同类型的for循环遍历——foreach、map date: 2025-01-04 11:02:17 tags: javascript 前端不同类型的for循环遍历 场景&#xff1a;很多时候后端发来的数据是不能够完全契合前端的需求的&#xff0c;比如你要一个数据对象中的值&#xff0c;但是这个值却作为了ke…

MR30分布式 IO 在物流分拣线的卓越应用

在当今物流行业高速发展的时代&#xff0c;物流分拣线的高效与精准运作至关重要&#xff0c;而其中对于货物点数较多情况下的有效控制更是一大关键环节。明达技术MR30分布式 IO 系统凭借其独特的优势&#xff0c;在物流分拣线中大放异彩&#xff0c;为实现精准的点数控制提供了…

5 分布式ID

这里讲一个比较常用的分布式防重复的ID生成策略&#xff0c;雪花算法 一个用户体量比较大的分布式系统必然伴随着分表分库&#xff0c;分机房部署&#xff0c;单体的部署方式肯定是承载不了这么大的体量。 雪花算法的结构说明 如下图所示: 雪花算法组成 从上图我们可以看…

Android wifi常见问题及分析

参考 Android Network/WiFi 那些事儿 前言 本文将讨论几个有意思的网络问题&#xff0c;同时介绍 Android 上常见WiFi 问题的分析思路。 网络基础Q & A 一. 网络分层缘由 分层想必大家很熟悉&#xff0c;是否想过为何需要这样分层&#xff1f; 网上大多都是介绍每一层…

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

音视频入门基础&#xff1a;MPEG2-PS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;1&#xff09;——MPEG2-PS官方文档下载 音视频入门基础&#xff1a;MPEG2-PS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ps文件 音视频入门基础…

读书笔记:分布式系统原理介绍

写在前面 已经大概三个月左右没有更新博客了&#xff0c;哈哈哈哈&#xff1b; 此博客是笔者在对《分布式系统原理介绍》进行概述&#xff0c;对于整个分布式系统协议的理解基于一些量化的指标考虑了数据的分布副本协议&#xff08;中心化/去中心化&#xff09;进行了总结&…