网络编程的实际案例

news2025/1/12 21:06:22

实现将英文转化称为中文

想在服务器和客户端中实现这一功能,在响应阶段做出让传进来的英文返回中文。需要定义一个HashMap类型来存储其可能输入进来的英文和将要返回的中文。

UDP中建立

UDP建立的细节在上一次写的博客当中

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

public class UdpDictServer extends UdpEchoServer  {//抛出异常的原因

    private Map<String,String> dict = new HashMap<>();
    public UdpDictServer(int port) throws SocketException {//在构造方法中填入可以保证当一运行这个服务器里面将会自动存储进这些翻译所需要的dict
        super(port);
        dict.put("dog", "小狗");
        dict.put("cat", "小猫");
        dict.put("pig", "小猪");
    }

    @Override
    public String process(String request) {//重写其继承的回显服务器中的process
        return dict.getOrDefault(request,"该词在字典中不存在");
    }

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

        UdpDictServer udpDictServer = new UdpDictServer(9090);
        udpDictServer.start();
    }

}

TCP中建立

TCP中的Socket API 和UDP中的Socket API有比较大的差异,但用有一些相同。

TCP中ServerSocket(给服务器使用的类,通过这个类来绑定端口号)

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

这两个类都是用来表示socket文件(抽象了网卡这样的硬件设备)

注因为TCP是字节流传输的基本单位是byte,不像TCP那样所以不用建立一个专门用来模拟数据报。

TCP中TCP是有连接的.UDP无连接所以TCP不用像UDP那样保存对应端口号的信息。

.

 private ServerSocket serverSocket = null;//通过这个对象来与客户端进行连接

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
   
            Socket socket =serverSocket.accept();//通过这个accept方法来接受客户端的信息,这个方法是自动完成的由操作系统内核(???)
        }
    }

TCP的基本思路与UDP一样在创建之初都需要创建一个对象来与客户端进行连接并进行保存,只不过TCP中不是直接用这个保存的对象来进行编写,而是又通过一个新的类来接受其中的信息,后续通过这个新的类创建的对象来进行交互。而UDP中是一个类来进行创建新的对象来进行接受信息和返回信息。

  public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            Socket socket =serverSocket.accept();//通过这个来接受客户端的信息
            processConnection(socket);//与UDP不同之处在于其是建立一个连接过程方法在里面进行处理各种信息
        }
    }
    public void processConnection(Socket clientSocket){

        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());//打印日志证明连接上了
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {//在try-with-resources语句的括号中声明的资源对象必须是实现了‘AutoCloseable`或`c1oseable`接口的对象,这些接口都定义了`c1ose()`方法。
            //Java编译器会在try-with-resources语句中自动生成对资源对象`close()'方法的调用,这些调用会被插入到finally块中,确保在try块执行结束后资源会被正确关闭。在try块执行过程中,如果发生异常,Java虚拟机会首先执行finally块中的代码,然后再向上抛出异常。这样就保证了资源的关闭操作一定会在异常被抛出之前执行,从而确保资源被正确关闭。
            while(true){
                   //下面代码规定了从inputStream这个字节流中读入数据千万不要写成System.in这是从键盘中读
                //Scanner scanner = new Scanner(System.in);//创建这个就是为了来接收从客户端传过来的字节流
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()){//使用hashNext来判断是否还有信息传过来如果没有那就是客户端关闭了
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;;
                }
            }

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

    }

在TCP中在客户端运行之后又不同的点在于因为TCP是直接与客户端通过字节流的方式进行连接的。所以需要对这字节流来进行处理,所以又建立了一个方法来进行控制,在使用字节流时又因为害怕流对象忘记关闭会导致资源泄露(???),使用Try来进行自动关闭,在使用try进行自动关闭时,try这一块的代码在运行完毕时,会自动生成        close方法,如然后这个方法会被插入到finall块中确保在try执行完之后会被关闭资源,并且如果发生异常的话,也会先执行finally块,然后在抛出异常的。

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

public class TcpEchoServer{


    private ServerSocket serverSocket = null;//通过这个对象来与客户端进行连接

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            Socket socket =serverSocket.accept();//通过这个来接受客户端的信息
            processConnection(socket);//与UDP不同之处在于其是建立一个连接过程方法在里面进行处理各种信息
        }
    }
    public void processConnection(Socket clientSocket){

        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());//打印日志证明连接上了
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {//在try-with-resources语句的括号中声明的资源对象必须是实现了‘AutoCloseable`或`c1oseable`接口的对象,这些接口都定义了`c1ose()`方法。
            //Java编译器会在try-with-resources语句中自动生成对资源对象`close()'方法的调用,这些调用会被插入到finally块中,确保在try块执行结束后资源会被正确关闭。在try块执行过程中,如果发生异常,Java虚拟机会首先执行finally块中的代码,然后再向上抛出异常。这样就保证了资源的关闭操作一定会在异常被抛出之前执行,从而确保资源被正确关闭。
            while(true){
                Scanner scanner = new Scanner(inputStream);//创建这个就是为了来接收从客户端传过来的字节流
                if(!scanner.hasNext()){//使用hashNext来判断是否还有信息传过来如果没有那就是客户端关闭了
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                String request = scanner.next();//将在字节流中读取到的信息传入到request中,并且使用next读的目的是因为next读时会因为读到空白符就会返回。空白符是一种特殊的字符:换行、回车符、空格指标符等在这里就规定当读到客户端的换行时为结束标志
                String response =process(request);//处理客户端传进来的数据返回一个响应
                PrintWriter printWriter = new PrintWriter(outputStream);//将会以文本的形式写入到outputStream
                printWriter.println(response);//使用PrintWriter将响应产生的字符串以文本形式写入outputStream中将其写进字节流中,并且这里使用printfln也是使客户端那里有回车,使客户端和服务器端有共同的逻辑来读数据
                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);
        }

    }

    public String process(String request) {
        // 此处也是写的回显服务器. 响应和请求是一样的.
        return request;
    }
    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}
PrintWriter printWriter = new PrintWriter(outputStream)其可以将下文中使用的printWriter.println(response)中的response读入到outputStream以文本形式进行传输。


 

  public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            Socket socket =serverSocket.accept();//通过这个来接受客户端的信息,sokcet这个对象循环每次有一个新的客户端来建立连接,都会建立相同的socket对像
            processConnection(socket);//与UDP不同之处在于其是建立一个连接过程方法在里面进行处理各种信息
        }
    }

在客户端不断的进行请求时会不断的创建新的socket对象,但是这个socket对象并没有被关闭,就会导致socket对象占据着文件描述符的位置。并且随着访问量的增多可能导致这个文件描述符慢导致导致文件泄露。

所以在最后我们自己进行关闭在原先加入下面代码进行手动关闭。

finally {


                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
         }

TCP的客户端

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

public class TcpEchoClient {
    private Socket socket = null;
    public TcpEchoClient(String sereveIp,int serevePort) throws IOException {

        socket =new Socket(sereveIp,serevePort);//连接时只需要创建socket对象并向这个对象里面传入服务器的值即可连接细节是内核来完成
    }
    public void  start(){
        Scanner scanner = new Scanner(System.in);//为了下面中读想要输入的内容
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream() )  {
            PrintWriter writer = new PrintWriter(outputStream);//为了下面将需求以文本类型输入outputStream
            Scanner scannerNetwork = new Scanner(inputStream);//为了下面读取服务器中的回复的响应
            while (true){
                System.out.println("->");
                String request = scanner.next();
                writer.println(request);//将用户的内容输入到outputStream字节流当中
                writer.flush();//将outputStream字节流中的东西冲刷进去服务器中
                String response = scannerNetwork.next();
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


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

tcp 的客户端行为和 udp 的客户端差不多.

从控制台读取用户输入的内容

把字符串作为请求, 发送给服务器

 从服务器读取响应.

 把响应显示到界面上.

但是现在还有代码问题不能建立一个多个客户端同时与服务器进行访问

如图所示如果有两个个进程的话第一个进行执行的时候代码会一直在processConnection方法中运行如果此时线程不输入请求,那么会使自己卡在scanner.hasNext(),并且processConnection这个方法不出去无法进行start方法中的true循环使,新的线程无法通过accept来建立于客户端的连接

导致其第二个线程无法运行。

解决这个问题可以加线程

线程是并发执行的互不影响

  public void start() throws IOException {
        System.out.println("服务器启动");
        while (true){
            //并且先输出2证明一直卡在accept的客户端信息,并且每次与一个客户端进行连接accept执行一次,其下面的进程每次都会在创建一个分支来运行processConnection等待这个分支的这个processConnection方法执行完了会使这个分支自己完
            System.out.println("1");//最开始服务器开始运行时启动执行1每当一个客户端运行时输出1那就说明这并不是在无线的输出1
            Socket socket =serverSocket.accept();//通过这个来接受客户端的信息,sokcet这个对象循环每次有一个新的客户端来建立连接,都会建立相同的socket对
            System.out.println("2");
            Thread t = new Thread(()->{//不用管其方法是否返回,每当一个请求过来accept都接受信息
                processConnection(socket);//与UDP不同之处在于其是建立一个连接过程方法在里面进行处理各种信息
            });
            t.start();
        }
    }

通过建立线程使这个循环可以不用管 processConnection返回的值了其每次建立一个对象时,循环自己都会再次到accepy等待下一个客户端对象。

还有一个方法使用线程池

ublic void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true){
            //并且先输出2证明一直卡在accept的客户端信息,并且每次与一个客户端进行连接accept执行一次,其下面的进程每次都会在创建一个分支来运行processConnection等待这个分支的这个processConnection方法执行完了会使这个分支自己完
            System.out.println("1");//最开始服务器开始运行时启动执行1每当一个客户端运行时输出1那就说明这并不是在无线的输出1
            Socket socket =serverSocket.accept();//通过这个来接受客户端的信息,sokcet这个对象循环每次有一个新的客户端来建立连接,都会建立相同的socket对
            System.out.println("2");
//            Thread t = new Thread(()->{//不用管其方法是否返回,每当一个请求过来accept都接受信息
//                processConnection(socket);//与UDP不同之处在于其是建立一个连接过程方法在里面进行处理各种信息
//            });
//            t.start();
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(socket);
                }
            });
        }
    }

线程池的方式是可以降低"频繁创建销毁线程的开销"但是,如果同一时刻有大量的客户端来连接,就会使系统上出现大量的线程如果一个机器有个几百个线程,还能勉强跑一跑.如果是上万个线程甚至更多,这个机器是非挂不可
 

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

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

相关文章

算法学习——LeetCode力扣补充篇1

算法学习——LeetCode力扣补充篇1 1365. 有多少小于当前数字的数字 1365. 有多少小于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&a…

大数据 - Spark系列《十五》- spark架构

Spark系列文章&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 大数据 - Spark系列《…

技巧 Win10电脑打开SMB协议共享文件,手机端查看

一. 打开 SMB1.0/CIFS文件共享支持 ⏹如下图所示&#xff0c;打开SMB1.0/CIFS文件共享支持 二. 开启网络发现 ⏹开启网络发现&#xff0c;确保共享的文件能在局域网内被发现 三. 共享文件夹到局域网 ⏹根据需要勾选需要共享的文件夹&#xff0c;共享到局域网 四. 共享文件查…

2024.3.26学习总结

一&#xff0c;正则匹配 正则匹配是用来搜索&#xff0c;匹配&#xff0c;替换的一种字符串模式&#xff0c;使用正则匹配可以让搜索匹配的语句更加简洁&#xff0c;在php中会使用一些函数来处理正则匹配 常用的语法&#xff1a; 字符类 [abc]: 匹配单个字符a、b或c[^abc]: 匹…

java的抽象类和接口

抽象类&#xff1a; abstract,可以用此关键字修饰类和方法 abstract修饰类就是抽象类&#xff0c;修饰方法就是抽象方法 抽象类的注意事项&#xff0c;特点&#xff1a;抽象类不一定有抽象方法&#xff0c;但有抽象方法的类一定是抽象类 类该有的成员&#xff08;成员变量&…

阿里云ECS经济型e实例,性价比超高的入门级云服务器!

阿里云服务器ECS经济型e系列是阿里云面向个人开发者、学生、小微企业&#xff0c;在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器&#xff0c;CPU处理器采用Intel Xeon Platinum架构处理器&#xff0c;支持1:1、1:2、1:4多种处理器内存配比&#xff0c…

在A中删除既在B表中出现又在C表中出现的元素

方法一&#xff08;感觉有点取巧&#xff0c;不太推荐&#xff0c;但是实现简单&#xff09;&#xff1a; 算法思想:保留La的头节点&#xff0c;并用pcur指针指向La链中的第一个结点&#xff0c;通过pcur指针遍历La中的每一个元素&#xff0c;并判断该元素是否在Lb和Lc链中出现…

腾讯云2核4G服务器优惠价格165元一年,限制500GB月流量

腾讯云轻量2核4G5M服务器租用价格165元1年、252元15个月、三年900元&#xff0c;配置为轻量2核4G5M、5M带宽、60GB SSD盘、500GB月流量、上海/广州/北京&#xff0c;腾讯云优惠活动 yunfuwuqiba.com/go/txy 腾讯云轻量2核4G5M服务器租用价格 腾讯云&#xff1a;轻量应用服务器1…

一篇搞定AVL树+旋转【附图详解旋转思想】

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

java AIO为什么用的并不多

Java AIO的本质是什么 原文&#xff1a;https://blog.csdn.net/hellojackjiang2011/article/details/131322757?spm1001.2101.3001.6650.1&utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-131322757-blog-103915337.235%5Ev43%5Epc_blo…

鸿蒙OS(ArkTS) 案例:【使用http网络请求框架加载验证码】

需求&#xff1a;加载验证码&#xff1b;1.下载验证码图像文件&#xff1b;2.获取header里面验证码ID 踩坑--踩坑--踩坑 根据文档使用 request.downloadFile 请求&#xff0c;官方示例: // pages/xxx.ets // 将网络资源文件下载到应用文件目录并读取一段内容 import common …

微信小程序开发【从入门到精通】——页面导航

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

【面试专题】JVM相关

1.为什么需要JVM&#xff0c;不要JVM可以吗&#xff1f; 1.JVM可以帮助我们屏蔽底层的操作系统 一次编译&#xff0c;到处运行 2.JVM可以运行Class文件 2.JDK&#xff0c;JRE以及JVM的关系 3.我们的编译器到底干了什么事&#xff1f; 仅仅是将我们的 .java 文件转换成了 .cl…

帆软报表在arm架构的linux

有朋友遇到一个问题在部署帆软报表时遇到报错。 问 我在 arm架构的linux服务器上部署帆软报表遇到了一个棘手的问题&#xff0c;你有空帮忙看下嘛。 我看后台日志报的错是 需要升级 gcc、libmawt.so &#xff0c;是系统中缺少Tomcat需要的依赖库&#xff0c;你之前处理过类似…

基于uQRCode封装的前端二维码生成组件实践

在前端开发中&#xff0c;二维码生成已成为一种常见需求。二维码凭借其简洁、方便的特点&#xff0c;被广泛应用于产品推广、信息交互等多个场景。在此背景下&#xff0c;开发一个易于使用且性能优越的二维码生成组件变得至关重要。本文基于uQRCode封装了一个前端二维码生成组件…

详解JAVA程序调优

目录 1.概述 2.命令 2.1.查看JAVA进程 2.2.查看虚拟机状态 2.3.查看线程的情况 3.工具 3.1.jconsole 3.2.jvisualVM 4.实战场景 1.概述 在实际工作中我们难免会遇见程序执行慢、线程死锁等一系列的问题&#xff0c;这时候就需要我们定位具体问题然后来解决问题了。所…

安科瑞路灯安全用电云平台解决方案【电不起火、电不伤人】

背景介绍 近年来 &#xff0c;随着城市规模的不断扩大 &#xff0c;路灯事业蓬勃发展。但有的地方因为观念、技术、管理等方面不完善 &#xff0c;由此引发了一系列安全问题。路灯点多面广 &#xff0c;一旦漏电就极容易造成严重的人身安全事故。不仅给受害者家庭带来痛苦 &am…

抽象类和接口的简单认识

目录 一、抽象类 1.什么是抽象类 2.抽象类的注意事项 3.抽象类与普通类的对比 二、接口 1.接口的简单使用 2.接口的特性 3.接口的使用案例 4.接口和抽象类的异同 一、抽象类 所谓抽象类&#xff0c;就是更加抽象的类&#xff0c;也就是说&#xff0c;这个类不能具体描…

雷卯有多种接口与电源保护方案

在当今的电子设备中&#xff0c;各种接口和电源保护至关重要。它们不仅关乎设备的正常运行&#xff0c;更直接影响到数据传输的稳定性和设备的安全。雷卯公司以其专业的技术和丰富的经验&#xff0c;为您提供全面的接口与电源保护方案&#xff0c;确保您的系统安全稳定运行。 …

图像分割论文阅读:Automatic Polyp Segmentation via Multi-scale Subtraction Network

这篇论文的主要内容是介绍了一种名为多尺度差值网络&#xff08;MSNet&#xff09;的自动息肉分割方法。 1&#xff0c;模型整体结构 整体结构包括编码器&#xff0c;解码器&#xff0c;编码器和解码器之间是多尺度差值模块模块&#xff08;MSM&#xff09;&#xff0c;以及一…