【网络编程】——基于TCP协议实现回显服务器及客户端

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【网络编程】【Java系列】
本专栏旨在分享学习网络编程的一点学习心得,欢迎大家在评论区交流讨论💌

目录

  • 一、TCP实现回显服务器
  • 二、服务器端
  • 三、客户端
  • 四、运行结果

一、TCP实现回显服务器

TCP提供的API主要有两个类Socket既会给服务器使用也会给客户端使用)和ServerSocket(一般为服务器使用)。TCP将数据分割成以字节为单位的小数据块进行传输(一个TCP数据报就是一个字节数组byte[])。

二、服务器端

在服务器端创建一个ServerSocket对象,并绑定一个端口号。
在这里插入图片描述

进入while循环:
注意这里和Udp实现回显服务器不同的是,Tcp进入循环之后并不是读取客户端请求,而是先处理客户端的连接(Tcp是一个有连接的协议(Udp是没有连接的协议),所以有连接的话会优先处理连接,连接可以理解为客户端和服务器彼此之间保留对方的信息)。一个服务器要应对很多客户端,在服务器内核中有很多客户端的连接,在应用程序层面我们需要对这些连接进行一一处理,这里服务器内核中的连接就像一个一个的待办事项一样,这些待办事项在队列这样的数据结构中,所以应用程序需要一一完成这样的任务(这个过程类似于生产者消费者模型)。
serverSocket.accept()方法可以将服务器内核中的连接获取到应用程序中
在这里插入图片描述

当程序启动之后就会立刻执行到accept方法,在调用serverSocket.accept()方法之后,如果此时没有客户端连接请求到达,该方法会一直阻塞,直到有客户端与服务器成功建立连接才会继续执行。
现在来解释这里的返回值问题,serverSocket.accept()方法会返回一个Socket对象:我们已经直到accept方法会把服务器内核已经建立好的连接拿到应用程序中,但是accpet方法的返回值并非是一个Connection这样的对象,而是Socket这样的对象
这里返回的Socket对象相当于一个耳麦,既可以发出自己的声音也可以听到对方的声音(我们不需要管对方是谁,我们只需要对着这个耳麦说话就行)。然后我们通过Socket对象与对方进行网络通信

SocketServerSocket
ServerSocket类用于在服务器端创建一个服务器套接字,它监听指定的端口,等待客户端的连接请求:通过调用ServerSocket的accept()方法,服务器监听指定端口,直到有客户端发起连接请求,accept()方法才返回一个表示客户端连接的Socket对象。通过这个Socket对象,可以进行与客户端的通信。
Socket类用于创建一个客户端套接字,它是客户端与服务器进行通信的终点:通过调用Socket的构造方法,指定服务器的主机名和端口号,可以与服务器建立连接。
总的来说:ServerSocket用于监听端口,接受客户端的连接请求并返回一个Socket对象,而Socket用于与服务器建立连接,并进行数据的读写操作。通过这两个类的配合,实现了TCP协议下的客户端与服务器之间的通信。

服务器端代码如下

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;
    private ExecutorService service = Executors.newCachedThreadPool();
    // 绑定端口号
    public  TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    // 启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!!!");
        while(true) {
            // 注意这里和Udp不同的是,Tcp进入循环之后并不是读取客户端请求,而是先处理客户端的连接
            // Tcp是一个有连接的协议(Udp是没有连接的协议),所以有连接的话会优先处理连接
            // 连接可以理解为客户端和服务器彼此之间保留对方的信息
            // 一个服务器要应对很多客户端,在服务器内核中有很多客户端的连接,在应用程序层面我们需要对这些连接进行一一处理
            // 这里服务器内核中的连接就像一个一个的待办事项一样,这些待办事项在队列这样的数据结构中
            // 所以应用程序需要一一完成这样的任务
            Socket clientSocket = serverSocket.accept();
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

    // 通过这个方法来处理连接的逻辑
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线啦!!!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        // 接下来读取请求,根据请求计算响应,最后再返回响应
        // Socket对象内部包含了两个字节流对象,我们需要先获取到这个字节流对象,然后再完成后续的读写操作
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 一次连接中可以涉及到多次请求和响应
            while( true ) {
                // 第一步:读取请求并进行解析
                // 这里为了读取方便,我们直接使用Scanner
                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);

                // 第三步:把响应写回给客户端,把OutputStream使用PrinterWriter进行包装,方法进行数据的传输
                PrintWriter writer = new PrintWriter(outputStream);
                writer.println(response);

                // 刷新缓冲区
                writer.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) {
                // 这里必须确保Socket能够被关闭
                e.printStackTrace();
            }
        }
    }

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

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

三、客户端

关于回显服务器这里我们来看客户端的代码是如何进行编写的,主要有4个步骤,如下:

  • 第一步:从控制台上读取用户的输入。
  • 第二部:把输入的内容构造成请求,并将其发送给服务器。
  • 第三步:从服务器读取响应
  • 最后一步:把响应显示到控制台上。

客户端代码如下:

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    private Socket socket = null;

    // 要和服务器进行通信的话我们就需要先知道服务器的位置外哪里。
    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        socket = new Socket(serverIp,serverPort);
    }

    public void start() {
        System.out.println("客户端启动!!!");
        Scanner scannerConsole = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while(true) {
                // 从控制台输入一个字符串
                System.out.print("-> ");
                String request = scannerConsole.next();
                // 把请求发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                // 使用println加上换行,后续服务器读取请求的时候就可以使用scanner.next来获取了
                printWriter.println(request);
                // 调用flush确保数据发送出去了
                printWriter.flush();
                // 从服务器中读取响应
                Scanner scannerNetwort = new Scanner(inputStream);
                String response = scannerNetwort.next();
                // 把响应打印出来
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

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

四、运行结果

运行结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文到这里就结束了,希望友友们可以支持一下一键三连哈。嗯,就到这里吧,再见啦!!!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python | Iter/genartor | 一文了解迭代器、生成器的含义\区别\优缺点

前提 一种技术的出现,需要考虑: 为了实现什么样的需求;遇到了什么样的问题;采用了什么样的方案;最终接近或达到了预期的效果。 概念 提前理解几个概念: 迭代 我们经常听到产品迭代、技术迭代、功能迭代…

echarts 切换时出现上一次图形残留。

先说结果:悬浮高亮导致。这可能使echarts的bug。 正常情况出现这种问题,一般是setOption 中没有配置notMerge 导致新的配置与旧配置合并。 但是我这里始终配置notMerge: true,但仍然出现这种问题。 最后发现与鼠标悬浮有关。 环境 echar…

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON)

英飞凌TC3xx之一起认识GTM(九)GTM相关知识简述(CMU,CCM,TBU,MON) 1 时钟管理单元(CMU)2 集群配置模块(CCM)3 时基单元(TBU)4 监控单元(MON)5 总结由前文的各篇内容,开发者已经知道如何使用GTM的大部分功能,在这些功能中,都需要一个信息就是fGTM 的数据,我们在前…

全网唯一值得推荐的C/C++框架和库

全网唯一值得推荐的C/C框架和库 C程序员开发指南 ​ 关注我,天天分享C/C开发技术干货! ​关注他 30 人赞同了该文章 ​ 目录 收起 标准库 C通用框架和库 人工智能 异步事件循环 音频 生态学 压缩 并发性 容器 数据库 调试 游戏引擎 图…

(03)光刻——半导体电路的绘制

01、绘制精细电路的第一步 金属-氧化物半导体场效应晶体管(MOSFET)的革命,让我们可以在相同面积的晶圆上同时制造出更多晶体管。MOSFET体积越小,单个 MOSFET的耗电量就越少,还可以制造出更多的晶体管,让其发挥作用,可谓是一举多得。可见,制造更小的MOSFET成了关键因素…

原来圣诞树可以这么做

先看结果 从上到下依次是: 2^0 2^1 2^2 2^3 2^4 2^5 2^6 2^7 ... 依次排下去,最后加4个单位数的数字 原来代码的世界里还有这个美。^V^

十大电脑屏幕监控软件超全盘点!

电脑屏幕已经成为我们工作、学习和生活中不可或缺的一部分。然而,随着人们对电脑使用的日益频繁,电脑屏幕监控软件也应运而生,成为了企业和个人用户进行电脑管理和监控的重要工具。 本文将为您盘点十大电脑屏幕监控软件,帮助您了…

软件测试基础理论学习-常见软件开发模型

瀑布模型 背景 瀑布模型的概念最早在1970年由软件工程师Winston W. Royce在其论文《Managing the Development of Large Software Systems》中提出。Royce虽然没有明确提出“瀑布模型”这个术语,但他描述了一种线性的、阶段性的开发流程,各个阶段之间具…

技术概述:ARMv8体系结构

John Goodacre, Director Program Management ARM Processor Division, November 2011 背景:ARM体系结构 从ARM精简指令集体系结构提出到现在已经有20多年了;ARMv7系列处理器是在ARMv4基础上设计的,随着ARMv7系列处理器大量应用&#xff0…

图像分割-Grabcut法(C#)

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 本文的VB版本请访问:图像分割-Grabcut法-CSDN博客 GrabCut是一种基于图像分割的技术,它可以用于将图像中的…

李沐机器学习系列5---循环神经网络

1 Introduction 对于样本的分析,通过全连接层处理表格数据,通过卷积神经网络处理图像数据;第一种假设,所有数据都是独立同分布的RNN 处理序列信号 序列数据的更多场景 1)用户使用习惯具有时间的先后性 2)外…

【Path的使用】Node.js中的使用Path模块操作文件路径

😁 作者简介:一名大四的学生,致力学习前端开发技术 ⭐️个人主页:夜宵饽饽的主页 ❔ 系列专栏:Node.js 👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇…

Leetcode算法系列| 12. 整数转罗马数字

目录 1.题目2.题解C# 解法一:模拟C# 解法二:硬编码数字 1.题目 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#xff0…

运算放大器(六):I-V 转换

1、跨阻放大器 放大器类型是根据其输入-输出信号的类型定义。假设放大器增益 (X:输入,Y:输出)。在电学范畴,由于用电压或电流表征一个信号,当输入信号为电流,输出信号为电压时&#…

MacOS14系统中Topaz Photo AI无法启动解决方法

MacOS14系统,在使用Topaz Photo AI是时无法启动,或者在 Mac电脑上导入图像后,Topaz Photo AI 应用程序窗口可能会冻结,怎么解决呢? 退出Topaz Photo AI for mac软件 回到电脑桌面,点击菜单栏前往-前往文件…

Prometheus 不能访问k8s的中的一些metrics的问题(controller-manager、scheduler、etcd)

主要有三个点 controller-manager、scheduler、etcd 参考: https://www.cnblogs.com/ltaodream/p/15448953.html kube-scheduler 在每台master节点执行 vim /etc/kubernetes/manifests/kube-scheduler.yaml 将 --bind-address127.0.0.1 改为 --bind-address…

Image - 体积最小的 base64 encode 1*1透明图片,透明背景图片base64编码

背景 前端开发时&#xff0c;有些<img>标签的src属性的值来源于接口&#xff0c;在接口获取结果之前&#xff0c;这个src应该设置为什么呢&#xff1f; 误区&#xff1a;设置为# 有人把src设置为<img src"#" />。 这是有问题的&#xff0c;浏览器解析…

理解UML中的依赖关系

理解UML中的依赖关系 在面向对象的设计中&#xff0c;理解各种类之间的关系对于构建一个清晰、可维护的系统至关重要。UML&#xff08;统一建模语言&#xff09;为我们提供了一种可视化这些关系的方式。今天&#xff0c;我们将深入探讨UML中的依赖关系&#xff08;Dependency&a…

脑电范式学习(一):Psychopy安装

脑电范式学习&#xff08;一&#xff09;&#xff1a;Psychopy安装 1 引言2 Psychopy软件3 安装教程4 花活儿5 总结 1 引言 可能有人会疑惑&#xff1a;为什么要去学Psychopy&#xff1f;Psychopy有什么好的&#xff1f; 首先&#xff0c;要告诉大家这么一个情况&#xff1a;现…

【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行环境搭建

【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 概述-CSDN博客 【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行环境搭建-CSDN博客 【大数据进阶第二阶段之Hadoop学习笔记】Hadoop 运行模式-CSDN博客 1、模板虚拟机环境准备 1.1、 hadoop100 虚拟机配置要求如下 &…