JavaEE 第20节 用TCP套接字实现简单回显服务器

news2024/9/22 4:02:54

这里写目录标题

  • 一、API介绍
    • ServerSocket
    • Socket
  • 二、创建简单的回显服务器
    • 服务器端
    • 客户端

一、API介绍

ServerSocket

  • 构造方法
方法签名方法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端⼝

关于此构造方法的注意事项:
Server的构造方法不止这一个,但是表格中的是最常用的。 ServerSocket(int
port) 不用指定服务器的IP地址,它会自动监听所有网络接口,在运行之前它的IP地址用通配符表示,服务器实际运行的IP地址取决于服务器的运行环境。 如果是在本地电脑,那么IP地址就是“127.0.0.1”,如果使用的是公网,那么IP地址就是公网IP,因此程序用就不用自己手动设置服务器的IP地址了

  • 类方法
方法签名方法说明
Socket accept开始监听指定端口(ServerSocket创建绑定的端口),有客户端连接后,返回一个服务端的Socket对象,并基于该Socket建立与客户但的连接,否则进入阻塞等待
void close()关闭套接字

注意:
只要使用套接字就会消耗文件资源,也就是会占用文件资源描述符。如果使用完套接字,不手动close()关闭,有时出现大量文件资源申请,文件资源描述符被使用完就可能出现文件资源泄露。为了保证代码的健壮性,一定要记得判断是否需要手动关闭文件资源。

Socket

  • 构造方法
方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接
  • 类方法
方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
in getPort()返回套接字所连接的端口号
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

二、创建简单的回显服务器

回显服务器(Echo Server)是一种简单的服务器应用程序,它的功能是接收客户端发送的数据,并将这些数据原样返回给客户端。这种服务器通常用于测试和调试网络应用程序。

服务器端

public class Server {

    //用于与客户端建立连接
    private ServerSocket serverSocket = null;

    public Server(int port) throws IOException {

        //这里只用设置端口号即可,不用设置IP地址,IP地址根据运行环境而定
        //因为是在本地运行,所以运行时IP地址时“127.0.0.1”
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");

        while (true) {
            //与UDP协议不同,TCP协议需要和客户端建立连接
            Socket clientSocket = serverSocket.accept();
            System.out.printf("服务器和客户端建立连接 [%s:%d]\n",
                    clientSocket.getInetAddress(),
                    clientSocket.getPort());

            //使用线程池,确保一个服务器可以同时处理多个客户端发来的请求
            Thread thread = new Thread(() -> {

                try {
                    //正式进行数据传输
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            //记得启动线程!
            thread.start();
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {

        //TC面向字节流,进行传输,所以使用OutputStream/InputStream
        try(OutputStream outputStream=clientSocket.getOutputStream();
            InputStream inputStream=clientSocket.getInputStream()) {

            //用于获取客户端的请求
            Scanner scanner=new Scanner(inputStream);

            //用于发送响应给客户端
            PrintWriter printWriter=new PrintWriter(outputStream);

            //循环处理请求与响应
           while(true){
           
               //1、获取客户端请求
               if(!scanner.hasNext()){

                   //如果没有得到客户端的请求,说明客户端下线了
                   break;
               }

               //得到的请求放到request中
               String request=scanner.next();

               //2、根据请求,生成响应
               String response=process(request);

               //3、把响应发送给客户端
               printWriter.println(response);
                printWriter.flush();//记得刷新缓冲区!!!详细解释见代码块下方注意事项

               //4、打印工作日志
               System.out.printf("[%s:%d] 请求=%s 响应=%s\n",
                       clientSocket.getInetAddress(),
                       clientSocket.getPort(),
                       request,
                       response);
           }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            //打印客户端下线信息
            System.out.printf("客户端下线 [%s:%d]\n",
                    clientSocket.getInetAddress(),
                    clientSocket.getPort());

            //只要是套接字,就会消耗资源占位符,如果用完没有及时关闭,可能出现文件资源泄露。
            if(clientSocket!=null&&!clientSocket.isClosed()){
                clientSocket.close();
            }
        }
    }

    //因为是回显服务器,所以这里直接返回相同的内容,在实际开发中这一步可能是比较复杂的
    private String process(String request){return request;}

    public static void main(String[] args) throws IOException {

        //自己指定一个端口号最好是大于1023的(当然也要小于65535)
        Server server=new Server(9090);
        server.start();
    }
}

注意事项:
PrintWriter的发送数据设置了缓冲区。 设置一个缓冲区的原因是避免频繁进行数据IO,因为这样消耗大量资源。通过缓冲区,数据堆积到一定大小在进行IO那么消耗成本就会小很多。但是在我们这个回显服务器中,需要立刻看到响应,所以需要用到PrintWriter中的flush()方法,调用这个方法可以刷新缓冲区,把所有的数据都进行IO。
 
另外, 除了用PrintWriter进行数据IO ,还可以使用outputStreamwrite()方法,但是需要把数据转化成字节数组:在这里插入图片描述

客户端

public class Client {

    //用于接收服务器的响应,或者发送请求给服务器
    private Socket socket=null;

    //两个参数分别是,需要连接的服务器的IP地址以及端口号,端口号刚才设置了是9090,由于是在本地运行所以ip默认是“127.0.0.1”
    public Client(String serverIP,int port) throws IOException {

        //和指定的服务器建立连接
        socket=new Socket(serverIP,port);
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");

        //与UDP不同,TCP面向字节流进行传输
        try(OutputStream outputStream=socket.getOutputStream();
            InputStream inputStream=socket.getInputStream()){

            //用户通过控制台输入,提供请求
            Scanner userIN=new Scanner(System.in);

            //用于发送请求
            PrintWriter printWriter=new PrintWriter(outputStream);

            //用于接收响应
            Scanner recieve=new Scanner(inputStream);

            //循环运行
            while(true){
                //1、获取用户请求
                System.out.print("->");
                String request=userIN.next();

                //2、把请求发送给客户端
                printWriter.println(request);
                printWriter.flush();//注意记得刷新缓冲区!

                //3、接收服务器发来的响应
                if(!recieve.hasNext()){
                    //没有获取到服务器的响应,可能是网络出现问题,或者服务器挂了
                    break;
                }
                 	//正确获取到响应
                String response=recieve.next();

                //4、把响应呈现给用户(打印)
                System.out.printf("响应:%s\n",response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        Client client=new Client("127.0.0.1",9090);
        client.start();
    }
}

最后,如果有任何关于代码或概念上的问题,欢迎私信或者评论交流。关于TCP协议的相关机制,可以参阅: 网络编程专栏 大家的支持是我前行的巨大动力!

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

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

相关文章

栈和队列的习题详解(1):有效的括号

前言: 在差不多二十天前小编写过栈和队列的详解,本来我想当时写完那两个结构之后就继续写它们的习题,但是写完那几篇博客以后,我就开始狂玩了十几天,我在上篇博客也说过,导致我在刚开学的时候就忘记了这个习…

mac的使用

mac使用python的问题 对于python的虚拟环境,其实是基于已经安装到本地的python来安装不同的包。(之前我的mac上只安装了python3.9.6 ,安装的位置为/usr/bin/python3)然后我在vscode里怎么找都找不到如何弄一个python3.7.6 的版本…

使用Pywin32和其他库控制Office软件进行自动化操作

目录 引言 Pywin32概述 基本概念 安装与配置 基本使用 Word自动化操作 文档创建与编辑 文档格式化 宏的运行 PowerPoint自动化操作 演示文稿的创建与编辑 幻灯片内容的格式化 高级应用:从Word自动生成PPT 读取Word文档中的内容。 保存生成的PowerPoi…

NetSuite AI 图生代码

去年的ChatGPT热潮期间,我们写过一篇文章说GTP辅助编程的事。 NetSuite GPT的辅助编程实践_如何打开netsuite: html script notes的视图-CSDN博客文章浏览阅读2.2k次,点赞4次,收藏3次。作为GPT综合症的一种表现,我们今朝来探究下…

SOMEIP_ETS_076: Wrong_Method_ID

测试目的: 验证当设备(DUT)接收到一个包含错误方法ID的SOME/IP请求时,是否能够返回错误消息或忽略该请求。 描述 本测试用例旨在检查DUT在处理一个echoUINT8方法的SOME/IP消息时,如果消息中包含的方法ID不正确&…

NC 寻找峰值

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 描述 给定一个长度…

12:以太网口模块布局

1.以太网口布局重点 ①两对差分线对应百兆网口,4对差分线对应千兆网口 ②以太网口重点是处理2对差分线,且优先走顶层 2.抽头信号要加粗:20mil-30mil

Unity学习路线

目录 一、Unity官方推荐路线二、AI总结的学习路线1、Unity学习路线图(文言一心)一、基础入门(初级)二、进阶提升(中级)三、高级深入(高级)四、专家级探索 注意事项 2、Unity学习路线…

【例003】利用MATLAB绘制有趣平面图形

题目: 用 ezplot 画出由方程 sin ⁡ ( x 2 m y 2 1000 ) cos ⁡ ( x y ) \sin(x^2\frac{my^2}{1000})\cos(xy) sin(x21000my2​)cos(xy) 确定隐函数的图形。 求解: 我们分别取m为100,1000,10000不同的值,绘制不同情况下的图…

计算机毕业设计选题推荐-公司考勤管理系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

解决银河麒麟中`netstat`命令不可用

解决银河麒麟中netstat命令不可用 1、问题2、解决方案3、 总结 💖The Begin💖点点关注,收藏不迷路💖 1、问题 在银河麒麟服务器操作系统中,netstat命令可能不可用,因为它属于未安装的net-tools软件包。 2…

LabVIEW程序员的护城河是什么

LabVIEW程序员的护城河在于他们深厚的行业经验和对特定领域的深刻理解,这使得他们在工业自动化、测试与测量等领域中难以被轻易取代。然而,随着AI技术的进步,部分基础性和重复性的工作可能会逐渐被AI接管,但LabVIEW程序员的独特技…

WEB渗透Win提权篇-合集(上)

完整20w字笔记: 夸克网盘分享 AppLocker GPO HKLM\SOFTWARE\Policies\Microsoft\Windows\SrpV2(keys:Appx、Dll、Exe、Msi 和脚本)。 列出 AppLocker 规则 PowerView PS C:\> Get-AppLockerPolicy -Effective | select -Expa…

0901python打印异常信息

python异常处理 1.目的2.python异常处理办法2.1直接打印异常信息2.2打印异常具体位置 3.日志处理3.1代码 4.结果输出 1.目的 在java中,我们经常会有统一异常处理以及日志打印模块,同样,python也是,在yolo中很多位置就是用到了这个,比如校验图片的尺寸,后缀名,设备状态等等… …

苹果笔记本电脑能不能玩游戏?苹果电脑玩游戏咋样?

过去Mac玩不了游戏最大的问题,就是图形API自成一体,苹果既不支持微软的DirectX,同时为了推广自家的Metal图形API,又对OpenGL和Vulkan两大主流的通用API敬而远之。游戏生态、硬件瓶颈让苹果电脑不适合玩游戏。 不过说到底&#xf…

C++ 设计模式——备忘录模式

C 设计模式——备忘录模式 C 设计模式——备忘录模式1. 主要组成成分2. 逐步构建备忘录模式步骤1: 创建备忘录步骤2: 实现原发器步骤3: 创建管理者(负责人)类步骤4: 客户端使用 3. 备忘录模式 UML 图UML 图解析 4. 备忘录模式的优点5. 备忘录模式的缺点6…

(1)冒泡排序和其优化

一 冒泡排序 1.1 冒泡排序概念 冒泡排序(Bubble Sort)是一种交换排序,基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序记录位置。 假设要对无序数列{2,3,4,5,6,7,8,1}排序:…

从一到无穷大 #34 从Columnar Storage Formats评估到时序存储格式的设计权衡

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 文章目录 引言Parquet / ORC功能与结构对比差异Indexes and Filters压缩影响 TsFile总结 引言 …

使用 Milvus Lite、Llama3 和 LlamaIndex 搭建 RAG 应用

大语言模型(LLM)已经展示出与人类交互并生成文本响应的卓越能力。这些模型可以执行各种自然语言任务,如翻译、概括、代码生成和信息检索等。 为完成这些任务,LLM 需要基于海量数据进行预训练。在这个过程中,LLM 基于给…