网络编程TCP

White graces:个人主页

🙉专栏推荐:Java入门知识🙉

🙉 内容推荐:Java网络编程(下)🙉

🐹今日诗词: 壮士当唱大风哥, 宵小之徒能几何?🐹


⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏

⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏


目录

TCP字节流套接字编程

回显服务器

服务端(有点问题的)

空白符问题

客户端

代码问题

原因

解决办法一

服务器(没问题的代码)

解决方法二

线程池的服务端代码(没问题的代码)

美图分享


TCP字节流套接字编程

TCP也有两个类用于网络编程

SeverSocket: 用于TCP字节数据进行网络通信, 创建了TCP服务端API, 专门用于服务器的

Socket: 客户端和服务器都能够使用, 客户端使用可以建立和服务器的通信

服务器可使用可以监听客户端的连接请求

值得注意的是: TCP是字节流传输, 传输单位是字节(byte), 不需要向UDP那样专门搞出一个类来用于传输数据报

SeverSocket类

构造方法: SeverSocket(int Port)

SeverSocket函数方法

函数方法

Socket类

构造方法: Socket(String host, int port)

Socket函数方法

回显服务器

服务端(有点问题的)

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpSever {
    private ServerSocket serverSocket = null;
    public TcpSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器!启动");
        while (true) {

            Socket clientsocket = serverSocket.accept();
            //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求
            processmethod(clientsocket);
            //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭

        }
    }

    private void processmethod(Socket clientsocket) throws IOException {
        System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort());
        //打印日志, 接下来就是响应请求了

        try (OutputStream outputStream = clientsocket.getOutputStream();
             InputStream inputStream = clientsocket.getInputStream()){
            //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法

            Scanner scanner = new Scanner(inputStream);

            while (true) {
                //客户端建立连接后, 可能会发送很多请求, 需要循环处理
                //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位

                if (!scanner.hasNext()) {
                    //读取前判断有没有数据, 没有数据说明连接断开了
                    System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort());
                    break;
                }

                String request = scanner.next();
                //读取请求, next()方法读取数据是读到空白符

                String response = process(request);
                //根据请求计算响应

                System.out.printf("[%s:%d] request = %s response = %s",
                        clientsocket.getInetAddress(), clientsocket.getPort(), request, response);
                //打印日志

                outputStream.write(response.getBytes());
                //将响应数据写回客户端

            }

        } finally {
            clientsocket.close();
            //客户端断开连接调用close方法释放资源
        }

    }

    private String process(String request) {
        //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上
        return request + "\n";
    }

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

空白符问题

空白符是一类分隔符的统称, 不是一个空格, 常见空白符有很多

TCP字节流传输常见的问题就是空白符问题

发送请求和读取请求时都需要考虑分隔符

因为发送的数据带有空白符,当使用next方法读取数据时, 会忽略空白符, 因此当我们向客户端写回请求时需要把空白符加回去, 保持格式的统一性

客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class TcpClient {
    private Socket socket = null;
    public TcpClient(String SeverIP, int SeverPort) throws IOException {
        socket = new Socket(SeverIP, SeverPort);
    }

    private void start() throws IOException {
        System.out.println("客户端, 启动!");
        Scanner scanner = new Scanner(System.in);

        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            //获取流对象, 把数据传入流对象之后, 等待服务器响应即可, 这里和UDP不同, UDP是把数据打包发送到服务器
            //而TCP客户端已经和服务器建立连接了, 中间通道已经打通, 将数据传入流对象等待响应即可
            //流对象扮演的角色就像是中间商, 服务器和客户端的操作都要经过流对象

            Scanner scannerinpuStream = new Scanner(inputStream);
            while (true) {
                System.out.print("请输入你要发送的请求: ");
                String request = scanner.next();
                request += "\n";
                //next方法会忽略\n, 为了保持服务端next方法一致, 手动加上\n

                outputStream.write(request.getBytes());
                //将请求写到流对象中

                if(!scannerinpuStream.hasNext()) {
                    //没有数据说明读取完毕了
                    break;
                }
                String response = scannerinpuStream.next();
                //从服务器获取响应

                System.out.println(response);
                //将数据打印出来

            }

        }

    }

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

运行效果

代码问题

这种写法只能对一个客户端提供服务,  当我们启动多个客户端时

原因

解决办法一

把processmethod方法放到多线程的环境下运行


服务器(没问题的代码)

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpSever {
    private ServerSocket serverSocket = null;
    public TcpSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器!启动");
        while (true) {
            Socket clientsocket = serverSocket.accept();
            //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求

            Thread thread = new Thread(() -> {
                try {
                    processmethod(clientsocket);
                    //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            thread.start();
        }
    }

    private void processmethod(Socket clientsocket) throws IOException {
        System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort());
        //打印日志, 接下来就是响应请求了

        try (OutputStream outputStream = clientsocket.getOutputStream();
             InputStream inputStream = clientsocket.getInputStream()){
            //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法

            Scanner scanner = new Scanner(inputStream);

            while (true) {
                //客户端建立连接后, 可能会发送很多请求, 需要循环处理
                //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位

                if (!scanner.hasNext()) {
                    //读取前判断有没有数据, 没有数据说明连接断开了
                    System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort());
                    break;
                }

                String request = scanner.next();
                //读取请求, next()方法读取数据是读到空白符

                String response = process(request);
                //根据请求计算响应

                System.out.printf("[%s:%d] request = %s response = %s",
                        clientsocket.getInetAddress(), clientsocket.getPort(), request, response);
                //打印日志

                outputStream.write(response.getBytes());
                //将响应数据写回客户端

            }

        } finally {
            clientsocket.close();
            //客户端断开连接调用close方法释放资源
        }

    }

    private String process(String request) {
        //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上
        return request + "\n";
    }

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

解决方法二

把代码放到线程池中, 这种方法更合适, 不仅解决了只能连接一个客户端, 又能减少线程创建销毁的开销

线程池的服务端代码(没问题的代码)

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

public class TcpSever {
    private ServerSocket serverSocket = null;
    public TcpSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器!启动");

        ExecutorService pool = Executors.newCachedThreadPool();
        //这个线程池可以自动扩容
        while (true) {

            Socket clientsocket = serverSocket.accept();
            //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processmethod(clientsocket);
                        //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    private void processmethod(Socket clientsocket) throws IOException {
        System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort());
        //打印日志, 接下来就是响应请求了

        try (OutputStream outputStream = clientsocket.getOutputStream();
             InputStream inputStream = clientsocket.getInputStream()){
            //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法

            Scanner scanner = new Scanner(inputStream);

            while (true) {
                //客户端建立连接后, 可能会发送很多请求, 需要循环处理
                //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位

                if (!scanner.hasNext()) {
                    //读取前判断有没有数据, 没有数据说明连接断开了
                    System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort());
                    break;
                }

                String request = scanner.next();
                //读取请求, next()方法读取数据是读到空白符

                String response = process(request);
                //根据请求计算响应

                System.out.printf("[%s:%d] request = %s response = %s",
                        clientsocket.getInetAddress(), clientsocket.getPort(), request, response);
                //打印日志

                outputStream.write(response.getBytes());
                //将响应数据写回客户端

            }

        } finally {
            clientsocket.close();
            //客户端断开连接调用close方法释放资源
        }

    }

    private String process(String request) {
        //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上
        return request + "\n";
    }

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


美图分享

✨🎆谢谢你的阅读和耐心!祝愿你在编程的道路上取得更多的成功与喜悦!"🎆✨🎄

⭐️点赞收藏加关注,学习知识不迷路⭐️

🎉✔️💪🎉✔️💪🎉✔️💪🎉✔️💪🎉

👍😏⛳️点赞☀️收藏⭐️关注😏👍

👍😏⛳️点赞☀️收藏⭐️关注😏👍

👍😏⛳️点赞☀️收藏⭐️关注😏👍

🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️🙆‍♂️

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

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

相关文章

Liunx音频

一. echo -e "\a" echo 通过向控制台喇叭设备发送字符来发声: echo -e "\a"(这里的 -e 选项允许解释反斜杠转义的字符,而 \a 是一个响铃(bell)字符) 二. beep 下载对应的包 yum -y install beep 发声命令 be…

Linux shell编程学习笔记55:hostname命令——获取或设置主机名,显示IP地址和DNS、NIS

0 前言 2024年的网络安全检查又开始了,对于使用基于Linux的国产电脑,我们可以编写一个脚本来收集系统的有关信息。其中主机名也是我们要收集的信息之一。 1. hostname命令 的功能、格式和选项说明 我们可以使用命令 hostname --help 来查看hostname命令…

[vue2项目]vue2+supermap[mapboxgl]+天地图之地图的基础操作(画线+自定义打点)

二、地图的基础操作 1、画线 案例(1) this.map.on("load", () => {let geometryLine = {type: "Feature",geometry: {// 定义类型type: "LineString",coordinates: [[113.39793764, 34.05675322],[113.35187554, 32.4392251],[112.476851…

电子电器架构 --- 什么是域控制器?

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

JAVA流程控制do...while循环

1.对于while语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次 2.do...while循环和while循环相似,不同的是,do...whlie循环至少会执行一次 do{ //代码语句 }while(…

961题库 北航计算机 操作系统 附答案 简答题形式

有题目和答案,没有解析,不懂的题问大模型即可,无偿分享。 第1组 习题 某计算机系统中的磁盘有 300 个柱面,每个柱面有 10 个磁道,每个磁道有 200个扇区,扇区大小为 512B。文件系统的每个簇(或…

王源演唱会火爆开枪

王源演唱会火爆开抢!当夜幕降临,繁星点点,无数粉丝的心随着一个名字而狂跳——王源!就在昨晚,王源的演唱会门票正式开抢,然而,就在这个激动人心的时刻,猫眼突然停止,让无…

Java集合:数据存储与操作的瑞士军刀

Java集合概述 集合是用来存储多个元素的容器。文章从四个方面来概述下集合,让读者对集合有一个大致的了解。 一、 多样化的容器 Java 集合大致包含 2 大体系 Collection体系 List:存储有序、重复的元素 Set:存储无序、不可重复的元素…

MySQL数据表的设计

实际工程中, 对于数据表的设计和创建, 我们遵循以下步骤: 首先确定实体, 找到关键名词, 提取关键信息, 设计表有哪些列, 每一列是什么. (有几个实体, 一般就创建几个表, 一般一个表对应一个实体) 实体之间的关系: 1. 一对一关系 例如: 一个学生, 只能有一个账号; 一个账号只…

simulink中调用C语言:S-Function

S函数的简单介绍 S函数:S-Function 可以使用 MATLAB, C, C, Ada,或 Fortran 语言来编写。使用 MEX 实用工具,将 C, C, Ada,和 Fortran 语言的 S-Function 编译成 MEX-文…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——03.差异表达基因筛选之热图绘制(4)

内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的相关性分析…

【C语言】文件操作(中卷)

前言 在文件操作(上卷)中,讲到的主要都是正式文件操作开始之前的前置知识,而这一卷中,我们将开始正式地操作文件。 在上卷中我们已经说到,stdin stdout stderr是三个C语言程序启动时默认打开的流。这三个流…

猫头虎分享已解决Bug || **Eslint插件安装问题Unable to resolve eslint-plugin-猫头虎

猫头虎分享已解决Bug || **Eslint插件安装问题Unable to resolve eslint-plugin-猫头虎 博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的…

cmake的基础使用

根据源程序文件生成可执行文件: 生成可执行文件: set设置变量: 指定输出路径: 搜索一个目录下的所有源文件: 制作静态库: 指定生成库的路径: 包含库文件: cmake中使用if语句: 使用me…

猫头虎分享已解决Bug || SyntaxError: Unexpected token < in JSON at position 0

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …

Python爬虫之简单学习BeautifulSoup库,学习获取的对象常用方法,实战豆瓣Top250

BeautifulSoup是一个非常流行的Python库,广泛应用于网络爬虫开发中,用于解析HTML和XML文档,以便于从中提取所需数据。它是进行网页内容抓取和数据挖掘的强大工具。 功能特性 易于使用: 提供简洁的API,使得即使是对网页结构不熟悉…

有损线、上升边退化与材料特性(七)

有损线的不良影响 当信号沿着实际有损线传输时,高频分量的幅度减小,而低频分量的幅度保持不变。由于这个种选择性的衰减,信号的带宽降低,信号的上升边会增长。如果上升边的退化与单位间隔比很小,同位模式将比较稳定与…

Redis学习笔记【实战篇--短信登录】

开篇导读 实战篇有什么样的内容 短信登录 这一块我们会使用redis共享session来实现 商户查询缓存 通过本章节,我们会理解缓存击穿,缓存穿透,缓存雪崩等问题,让小伙伴的对于这些概念的理解不仅仅是停留在概念上,更…

【多模态】34、LLaVA-v1.5 | 微软开源,用极简框架来实现高效的多模态 LMM 模型

文章目录 一、背景二、方法2.1 提升点2.2 训练样本 三、效果3.1 整体效果对比3.2 模型对于 zero-shot 形式的指令的结果生成能力3.3 模型对于 zero-shot 多语言的能力3.4 限制 四、训练4.1 数据4.2 超参 五、代码 论文:Improved Baselines with Visual Instruction …

什么是ACP?

前言 ACP指的是应用程序控制平面,是微服务架构中的一个关键组成部分。它负责管理微服务架构中的各个微服务,包括服务发现和注册、负载均衡、服务路由、熔断和降级、配置管理等方面的功能。 A:可用性 所有请求都有响应。C:强一致…