网络编程TCP

news2024/11/19 11:28:48
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();
    }
}


美图分享

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

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

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

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

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

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

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

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

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

相关文章

[职场] 为什么不能加薪? #学习方法#知识分享#微信

为什么不能加薪? 不能加薪的根本原因,终于被我找到了! 朋友们!职场这个地方是个很神奇的世界,有些规则并不是你想象的那样。我们都希望能在这个世界里施展自己的才华,获得升职加薪的荣耀。然而&#xff0c…

Docker搭建ELKF日志分析系统

Docker搭建ELKF日志分析系统 文章目录 Docker搭建ELKF日志分析系统资源列表基础环境一、系统环境准备1.1、创建所需的映射目录1.2、修改系统参数1.3、单击创建elk-kgc网络桥接 二、基于Dockerfile构建Elasticsearch镜像2.1、创建Elasticsearch工作目录2.2、上传资源到指定工作路…

Maxkb玩转大语言模型

Maxkb玩转大语言模型 随着国外大语言模型llama3的发布,搭建本地个人免费“人工智能”变得越来越简单,今天博主分享使用Max搭建本地的个人聊天式对话及个人本地知识域的搭建。 1.安装Maxkb开源应用 github docker快速安装 docker run -d --namemaxkb -p 8…

被拖欠6000万,代工大厂倒闭了····

6月7日消息,据综合媒体报道,电子烟代工大厂佳斯特电子(东莞)发布公告,将自6月3日起停止一切经营活动,开始自行清算工作。根据佳斯特电子科技(东莞)有限公司的通告,由于公…

IP域名关系的研究与系统设计(学习某知名测绘系统)

IP域名关系库管理包括域名库检索和whois库检索,详情如下。 域名库检索支持以下5项功能: 1.通过过滤器检索 筛选条件包含IP地址、口令、工具名称、可利用的漏洞编号、创建时间; 2.通过关键字检索 在查询框中输入域名库名称的部分关键词&a…

基于语音识别的智能电子病历(其他)网络电话的接入

医生在使用语音识别时有以下几种方式。 医院或者医生通过接口或者网站上传语音文件医生使用电话进行录音,医生拨打电话进行录音。有T1线路语音卡,也有网络电话的接入。医生使用手机APP进行录音。 下面说一下网络电话的使用,(注意…

C语言数据结构(排序算法总结)

目录 算法类型 算法比较 稳定性描述 插入排序 选择排序 冒泡排序 希尔排序 堆排序 快速排序 霍尔排序(递归) 挖坑法(递归) 双指针(递归) 快排(非递归) 归并排序 计数排序 总结(速…

计算机毕业设计基于YOLOv8的头盔检测系统

1、安装Anaconda 官网下载或者哔哩哔哩有的up分享 https://www.anaconda.com/download 版本无所谓,安装位置不要有中文就行 2、创建环境yolov8 winR打开命令行 conda create -n yolov8 python3.9 3、打开源码 下载下来放到你想放的目录,直接用pyCharm或者…

华为端云一体化开发 (起步1.0)(HarmonyOS学习第七课)

官方文献: 为丰富HarmonyOS对云端开发的支持、实现端云联动,DevEco Studio推出了云开发功能,开发者在创建工程时选择云开发模板,即可在DevEco Studio内同时完成HarmonyOS应用/元服务的端侧与云侧开发,体验端云一体化协…

【排序】插入排序,希尔排序

前面我们讲述了冒泡排序和选择排序,我们本章讲的排序方法是插入排序,插入排序是希尔排序实现的基础函数,大家一定要好好理解插入排序的逻辑,这样才能在后面学习希尔排序的时候,更容易的去理解,我们直接开始…

Vue根据后端返回的tabList动态渲染组件信息

最近做了一个功能&#xff0c;后端根据配置信息&#xff0c;动态返回一个tabList&#xff0c;其中结构是List<String,Object> tabList; map里面的数据是 label、value 页面需要根据tablist动态渲染组件&#xff08;不同的tab都使用了组件进行了封装&#xff09; 实现效果…

[原创][Delphi多线程]TThreadedQueue的经典使用案例.

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delph…

Git介绍及应用

1.简介 Git是一个分布式版本控制器&#xff0c;通常用来对软件开发过程中的源代码文件进行管理。通过Git仓库来存储和管理这些文件&#xff0c;Git仓库分为两种&#xff1a; 本地仓库:开发人员自己电脑上的Git仓库远程仓库:远程服务器上的Git仓库 2.执行流程 3.Git代码托管服务…

NFT Insider #133:苏富比将拍卖BAYC系列NFT,Taiko创世NFT系列已上线

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members &#xff08;https://twitter.com/WHALEMembers&#xff09;、BeepCrypto &#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜…

360误删掉的文件怎么恢复?3种文件找回办法!

360是一款电脑安全辅助软件&#xff0c;可以用它来清理垃圾、查杀病毒、修复系统等。然而&#xff0c;有时文件可能会被360误认为是垃圾而自动清理&#xff0c;造成重要文件丢失&#xff0c;那么360删掉的文件怎么恢复&#xff1f;下面&#xff0c;让我们一起来看看如何恢复文件…

Java面试题:Redis持久化问题

Redis持久化问题 RDB (Redis Database Backup File) Redis数据快照 将内存中的所有数据都记录到磁盘中做快照 当Redis实例故障重启时,从磁盘读取快照文件恢复数据 使用 save/bgsave命令进行手动快照 save使用主进程执行RDB,对所有命令都进行阻塞 bgsave使用子进程执行R…

Java——面向对象进阶(一)

前言 面向对象进阶(一)&#xff1a;static&#xff0c;继承&#xff0c;this和super关键字 文章目录 一、static1.1 静态变量1.2 静态方法1.3 静态变量和静态方法在内存中 二、继承2.1 概念2.2 继承的特点和能继承什么2.3 继承中的重写2.4 this和super关键字 一、static 在 Jav…

基于拓扑漏洞分析的网络安全态势感知模型

漏洞态势分析是指通过获取网络系统中的漏洞信息、拓扑信息、攻击信息等&#xff0c;分析网络资产可能遭受的安全威胁以及预测攻击者利用漏洞可能发动的攻击&#xff0c;构建拓扑漏洞图&#xff0c;展示网络中可能存在的薄弱环节&#xff0c;以此来评估网络安全状态。 在网络安…

如何使用Vuforia AR进行增强现实技术的开发?

前言 今天是坚持写博客的第17天&#xff0c;很高兴自己可以一直坚持下来。我们今天来讲讲怎么使用Vuforia AR进行增强现实的开发。 我们需要在今天的开发中用到Vuforia AR和2018版的Unity3d 什么是Vuforia AR Vuforia AR是基于实时计算摄影机影像的位置及角度&#xff0c;并…

【Kubernetes】三证集齐 Kubernetes实现资源超卖(附镜像包)

目录 插叙前言一、思考和原理二、实现步骤0. 资料包1. TLS证书签发2. 使用 certmanager 生成签发证书3. 获取secret的内容 并替换CA_BUNDLE4.部署svc deploy 三、测试验证1. 观察pod情况2. 给node 打上不需要超售的标签【可以让master节点资源不超卖】3. 资源实现超卖4. 删除还…