网络编程UDP+TCP

news2024/9/26 5:12:26

日升时奋斗,日落时自省 

目录

1、网络编程基本概念

2、UDP数据报套接字编程

2.1、UDP相关API

2.1.1、DatagramSocket API

2.1.2、DatagramPacket API

 2.2、UDP版本服务器

 2.3、UDP版本客户端

 2.4、UDP连接操作

2.5、翻译业务

2.6、总结

 3、TCP流套接字编程

3.1、TCP相关API

3.2、TCP版本服务器

 3.3、TCP版本的客户端

 3.4、TCP连接操作

3.4.1多线程TCP服务器

3.4.2、线程池TCP服务器


1、网络编程基本概念

网络编程指的是网络上的主机,通过不同的进程,以编程的方式进行实现网络通信

详细下来就是我们只要满足进程不同就行;所以即使是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程

(1)发送端和接收端

发送端:数据的发送方进程 ,称为发送端,发送端就是网络通信的源主机

接收端:数据的接收方进程, 称为接收端,接收端就是网络通信中的目的主机

注:发送端和接收端只是相对的。

(2)请求和响应

一般情况,获取一个网络资源,涉及到两次网络数据传输

首先: 请求数据发送

然后:响应数据发送

例如 :餐馆买饭,客人发出请求(我要吃什么),餐馆针对请求做出处理(后厨颠勺中),最后做好针对客人进行响应(做好了给你)

 (3)客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务

客户端:获取服务的一方进程,称为客户端

注:我们常说的客户端(发送端)和服务器(接收端),但是理解不单就是客户与服务器交互,如果是服务器与服务器交互谁又是客户端(发送端)其实就是看谁给谁发送请求,发送请求的就是发送端,接收请求的就是接收端

2、UDP数据报套接字编程

2.1、UDP相关API

基于UDP来编写一个简单的客户端服务器进行网络通信程序;需要有一定的网络基础

首先就是要认识进行网络编程要使用的API

核心内容 Socket API,操作系统给应用程序提供的网络编程API;Socket这里翻译成套接字,它是计算机之间进行通信的一种约定或一种方式;虽然socket英译为“插座”,但是在计算机领域里它叫“套接字”。

可以认为socket API是和传输层密切相关的,传输层提供了两个最核心的协议UDP和TCP所以socket也提供了两种风格的API

DatagramSocket使用这个类,表示一个socket对象,代表socket文件;原因:在操作系统中,把这个socket对象也是当成了一个文件来处理,相当于是文件描述符表上的一项,提到这里就会想到普通文件也就是对应的硬件设备(硬盘),socket文件,对应的硬件设备(网卡)一切都是文件而起。

一个socket对象,就可以和另外一台主机进行通信,如果要和多个不同的主机通信,需要创建多个socket对象。

2.1.1、DatagramSocket API

DatagramSocket是UDP Socket用于发送和接收UDP数据报 (这里稍微有点绕口,多看两遍)

这个绕口的话是解释:DatagramSocket与下面马上要提到DatagramPacket对比理解,实际意思就是DatagramSocket里面是数据报作为参数也就是DatagramPacket类的(它就是作参数的)

(1)无参数版本的构造方法,没有指定端口,系统会自动分配一个空闲的端口

 (2)一个参数版本的构造方法,有一个端口号,此时就是为了socket对象能和指定的端口号(简单的数字范围在1024-65535之间),关联起来;我们知道端口号是和进程相关连的,为了确定是哪个进程,本质上不是进程和端口号建立联系,而是进程中的socket对象和端口建立了联系

(3)需要了解的方法 一个receive(接收)另一个是send(发送)

 receive方法此处有一个DatagramPacket的参数,但是这里填的相当于是一个空对象,receive方法内部会对参数的这个空对象进行内容填充(将接收来的数据填充),从而构造出结果数据,参数也是一个“输出型参数”  如果没有接收到数据 会进行阻塞等待

send方法也有一个DatagramPacket参数,这里的参数就有数据了,从此套接字发送数据报

 最后一个使用需用方法就是close 关闭此数据报套接字

2.1.2、DatagramPacket API

DatagramPacket是UDP socket发送和接收的数据报

DatagramPacket表示UDP中传输的一个报文,构造这个对象可以指定一些具体的数据进去,这些数据后面实现UDP的时候会展示

(1)2个参数版本的构造方法

把buf这个缓冲区给设置进去,数据存储先放在缓存区上,既然是文件就需要涉及到文件操作,当然内部会自己处理,我们直接使用网络编程相关API就可以了(最后一个参数是缓冲区设置的长度)

(2)4个参数版本的构造方法

 构造缓冲区、起始位置、缓冲区长度、地址(这个地址包括两个内容IP地址 和 端口号port)

这里提到一个InetSocketAddress 的API 它也有自己得构造方法,里面包含两个参数一个IP地址,另一个是端口号

 2.2、UDP版本服务器

我们这里做的是自己本机电脑的服务器,就是本机连接本机 称为回显服务器(echo server)

一个普通的服务器的构造需要涉及这几步:收到请求,根据请求计算响应,返回响应

Echo server 省略了其中的“根据请求计算响应”,实现的这个程序是请求啥返回啥,主要针对socket API基本用法的理解,这里不写“请求计算响应”不是它不重要,它是最重要的,但是实现需要对应的业务,这个等socket会用了再实现一个比较简单业务给友友们理解

提示:我们这里自定义了一个类 叫做: UdpEchoServer 以下UDP服务器代码都是包含在这个类中

第一步:

网络编程操作本身就是操作网卡的,但是网卡不方便直接操作,在操作系统内核中,使用了一个特殊的叫做“socket”这样的文件来抽象表示网卡,因此进行网络通信是势必要有一个Socket

private DatagramSocket socket=null;

第二步:

 这里是我们自己实现一个UDP版本的服务器,socket当前置空的原因是为了在该类的构造方法中添加输入自己的端口号,创建socket对象的同时,给他绑定一个端口号;

public UdpEchoServer(int port) throws SocketException {
        //构造方法中有一个参数就是 端口号 在socket创建对象的时候传入端口号
        socket=new DatagramSocket(port);
    }

为什么要关联一个具体端口号???

因为客户端是需要通过服务器的端口号来给服务器发送请求,那前面不是说如果不分配的话操作系统会分配一个随机的端口号吗?如果是这样的话,客户端就不知道这个端口号是什么了,也就没有办法进行通信了。

第三步:

思路:
(1)创建一个执行start方法,因为客户端肯定是不会就发发送一次请求的,会有多次,所以服务器一般都是7*24小时工作的,这里会写一个while(true)循环保持服务器不会断开.

(2)那循环内需要的就是网络所需的基础 : 接收请求 , 根据请求计算响应  ,  返回响应(这里我们细说)

接收请求:

针对UDP来说传输数据的基本单位是"数据报" 对应的类就是DatagramPacket,首先就是读取客户端的请求,需要使用receive方法 ,该方法的参数是一个输出型参数,这个参数就要是一个空的DatagramPacket对象,需要DatagramPacket对象创建对象

 DatagramPacket对象中传了两个参数 ,一个缓冲区,另一个该对象缓冲区的的长度;创建对象后传参给receive,如果客户端有数据传入receive内部会针对参数对象填充数据(该数据来自网卡),如果客户端没有数据传入receive会进行阻塞

 根据请求计算响应:

此时DatagramPacket是一个特殊的对象,并不直接进行数据处理,可以把来面的数据转化为字符串进行处理

获取字符串之后,就可以进行处理了(这里获取字符串的原因就是为了便于我们做处理);

 因为我们这里是走一下流程,所以没有写请求处理,知道要写这个就行.

返回响应:

把响应写回客户端,使用方法 send的参数也是DatagramPacket 对象,需要把返回对象构造好,此处构造对象就不在是空字节数了,而是使用响应数据来构造的

 到了这里UDP的客户端就写完了.(以上都是图片方便看解析,下面附有代码)

public void start() throws IOException {
        System.out.println("服务器启动");
        //服务器不是只给一个客户端提供服务系统就完了,一定会有很多的客户端
        while(true){
            /*
            * 只要客户端来了,就提供服务
            * 1、读取客户端发来的请求是啥
            * receive 方法的参数是一个输出型参数 需要先构造一个空白的DatagramPacket对象 ,交给receive来进行填充
            * */
            DatagramPacket requestPacker=new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacker);  //将接受传来的数据放入的当前的缓冲区
            //此时这个 DatagramPacket 是一个特殊的对象, 并不方便直接进行处理 可以把这里包含的数据拿出来,构造成一个字符串
            String requset =new String (requestPacker.getData(),0,requestPacker.getLength());
            //2、根据请求计算响应, 由于此处是回显服务器 ,响应和请求相同
            String response=process(requset);
            /*
            * 3、把响应写回到客户端 ,send的参数也是 DatagramPacket 需要把这个 Packet对象构造好
            * 此处构造的响应对象 不能是用空的字节数 构造了,而是要使用响应数据来构造
            * */
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacker.getSocketAddress());
            socket.send(responsePacket);
            //4、打印一下, 当前这次请求响应处理中间结束
            System.out.printf("[%s: %d] req:%s ,resp:%s\n",responsePacket.getSocketAddress().toString(),
                    responsePacket.getPort(),requset,response);
        }
    }
    public String process(String requset) {
        return requset;
    }

最后一行还进行了打印,这里也对其进行解释:

 main方法操作:

 2.3、UDP版本客户端

这里的客户端是为了连接上面的服务器,待这里客户端写好以后友友们就可以自行尝试。

提示:这里客户端创建一个自定义类我这里叫做: UdpEchoClient 以下所有UDP客户端代码都在这个类中

第一步:
创建我们需要的变量 UDP客户端也需要socket文件来传输数据 ,需要创建一个 socket对象,同时还要知道服务器的IP地址和 端口号才能发送数据。

    private DatagramSocket socket=null;
    private  String serverIP=null;
    private  int serverPort=0;
    /*
    * 一次通信 ,需要有两个 IP 两个端口
    * 客户端 IP 是 127.0.0.1
    * 客户端的 port 是系统自动分配的
    * 服务器 IP 和 端口 也需要告诉客户端 ,才能顺利把这个消息发给服务器
    * */
    /*
    * 客户端这里为啥没有 个一个具体的端号  因为服务器不是本机 客户的主机上可能已经运行了很多的程序就会导致你设置的端口 号是被重复,会产生冲突
    * */
    public  UdpEchoClient(String serverIP ,int serverPort) throws SocketException {
        socket=new DatagramSocket();
        this.serverIP=serverIP;
        this.serverPort=serverPort;
    }

这里可能会有友友们问到为啥服务器有自己自己的端口号,这里为什么没有提到客户端具体的端口号??

答案:客户端的端口号是系统自动分配的(分配一个空闲的端口号),那可以自己给客户端定义一个具体的端口号吗?勉强可以,因为客户端上有可能有很多程序,每个程序都有自己的端口号,如果自己定义就可能会产生冲突;

举个例子:你去吃饭,人多的时候就会给你一个牌牌,叫到你,你来领餐,但是此时你就是想要66号牌牌,但是可能这个牌牌已经被人领走了(该饭店就是一个客户端,餐牌就是端口号,客户端会系统自动分配端口号,你强行要一个餐牌就可能已经被(其他程序)拿走了造成冲突)

第二步:

写一个start方法进行运行 :思路服务器大同小异(反向操作)

 while(true)循环中执行:写数据进行判断结束、发送数据、接收数据、控制台打印

写数据进行判断:

 发送数据:

 接收数据:

 控制台打印:

 以上图片解析比较零散,以下附有以上解释的代码

public void start() throws IOException {
        System.out.println("客户端启动 ");
        Scanner scanner=new Scanner(System.in);
        while(true){
            //1、从控制台读取发送的数据
            System.out.println("> ");
            String request=scanner.next();
            if(request.equals("exit")){
                System.out.println("结束");
                break;
            }
            /*
            * 2、构造成UDP 请求 并发送
            *   构造这个Packet 的时候 ,需要把serverIP 和 port 都传入过来 但是此处IP地址是一个32位的二级制的整数
            *   上述的IP地址是一个字符串 需要使用 InetAddress.getByName来进行一个转换
            * */
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);
            //3、读取服务器的UDP 响应 并解析
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response=new String(responsePacket.getData(),0,responsePacket.getLength());
            //4、把解析好的结果显示出来
            System.out.println(response);
        }
    }

main方法操作:

和UDP服务器操作基本相同,但是需要 服务器的IP地址和服务器的端口号

 2.4、UDP连接操作

首先这里先说怎么检测你的服务器和客户端都是写好的可以运行了:

以上连接操作是怎么进行,代码详细分解

2.5、翻译业务

以下带有业务的服务器继承了上面UDP版本服务器进行的

    //字典服务器 用来继承既可以
    //DictServer 来说 和 EchoServer 相比 大部分的东西都是一样的
    //主要是请求计算机响应是不一样的
public class UdpDictServer extends  UdpEchoServer {
    //这里是一个简单的翻译  使用到了Map  第一个泛型参数是接收单词 第二个泛型参数是 翻译过来的汉语
    private Map<String ,String> dict=new HashMap<>();
    //应用服务器这里需要继承父类 构造方法里面就可以写对应的东西
    public UdpDictServer(int port) throws SocketException {
        super(port);
        /*
        * 给当前 继承服务器 设置内容
        * 此处可以无限键值对  以下就是翻译内容 当然这里只是为了方便友友们看, 实际自己想实现点简单的业务也是可以的
        * */
        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("fuck","卧槽");
    }
    //继承了也就需要 重写 UdpEchoServer的方法
    public String process(String request){
        //查词典的过程
        return dict.getOrDefault(request,"当前单词没有查到结果");
        //这里就是 看是否是默认值 , 如果有就是用当前值 如没有的话就是用默认值
    }
    public static void main(String[] args) throws IOException {
        //本次只需要掉用该服务器就可以了
        UdpDictServer server=new UdpDictServer(9090);
        server.start();
    }
}

这里就是简单的业务实现,上面给了注释,代码不多也很好理解,重点理解前面的服务器和客户端代码,这里就不难看懂了。

以下演示以下,该带有业务的服务器跑起来 与客户端 联用

2.6、总结

(1)服务器的端口 是固定指定的,目的是为了方便客户找到服务器程序

(服务器是我们自己手里的机器,上面运行啥,都是我们控制的,指定安排空闲端口即可)

从哪里看 ,可以有两个地方看: 任务管理器友友们应该都很熟悉(Ctrl+alt+del 后点击任务管理器)以下点击就能查看PID(Process ID)其实是进程号(也是独一无二的)

 另一种方法就是命令行查看 涉及cmd命令 netstat -ano  (命令行打开操作: win+r 弹出框输入cmd)

 这里只截取了一部分作为演示,能理解就行

(2)客户端的端口 是由系统分自动分配的,如果手动指定,可能能会造成其他程序的端口的冲突

(客户端上的程序是不可控的,系统分配是最好的,服务器上的端口号是可控的,因为程序员可以自己看见,或者自查)

(3)端口冲突

端口冲突会有以下提醒 ,这里以服务器作为当前的端口冲突来解释

上面已经提及了两个服务器,一个UPD版本的服务器,另一个翻译业务的服务器 两个服务器同时用一个9090端口号就会产生冲突

 3、TCP流套接字编程

3.1、TCP相关API

TCP API主要涉及两个类

ServerSocket 专门给服务器使用的Socket对象 

Socket 是既能给 客户端是用也能给服务器使用 (知道这里不是很好理解,往后面看结合代码理解)

TCP和UDP传输数据不一样 UDP是“数据报” 但是TCP不是 而是以“字节”的方式 流式传输

ServerSocket一个参数构造方法

 创建一个服务器套接字Socket,并绑定到指定端口

使用方法就有所变化 ,检测方法Socket.accept()开始监听指定端口,有客户端;连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接 ,否则阻塞等待 ,这里明显与UDP的服务器不同了,体现了TCP的有连接的特性

关闭此套接字 close()方法

这里相当于是连接,看是否连接上客户端,TCP 有一个特性就是有连接的,例如电话一样,需要有人接,所以这个accept就是监听有没有人接电话,没有的话就先进行阻塞(这里只是以电话举例,不是说电话就是这么搞的)

Socket在服务器这边,是由accept返回的,在客户端这边,咱们代码里构造的时候制定了一个IP地址和端口号,有了这两个信息就能和服务器建立连接了。

既然是以字节的方式进行传输的,那就会涉及到IO文件操作 InputStream对应getInputStream方法和OutputStream对应的getOutputStream方法 进一步通过Socket对象,获取到内部的流对象,借助流对象来进行发送接收

3.2、TCP版本服务器

提示:这里需要我们自定义一个类叫做:TcpEchoServer来写TCP版本服务器的代码

第一步:就是创建一个ServerSocket 对象 创建一个构造方法进行传参一个具体的端口号

//相当于是  接收客户来
    private ServerSocket serverSocket=null;
    public  TcpEchoServer(int port) throws IOException {
        //传入一个端口号
        serverSocket =new ServerSocket(port);
    }

 第二步:那服务器ServerSocket对象已经创建好了,需要一个方法来进行服务器start方法

public void start() throws IOException {
        System.out.println("启动服务器");
        while(true){
            //使用这个 clientSocket 和 具体的的客户端进行交流
            //具体进行服务器  这里用作监听 客户端是否上线
            Socket clientSocket =serverSocket.accept();
            //如果上线  accept方法 阻塞结束 进行连接操作
            processConnection(clientSocket);
        }
    }

每次创建或者返回一个 Socket对象 ,(Socket就是文件)每次创建一个clientSocket对象,就要占用一个文件描述符表,使用完毕之后就会进行“释放” ,但是不建议直接在这里“释放”,在finally里面“释放”

到了这里就会友友问 :ServerSocket 与Socket都是创建对象这不是多此一举吗?答案:这里其实我也不知道(凡是存在必有道理),我们先按照这样的方法来。

但是这里还是要说一下,这里该如何理解。

 第三步:processConnection如何来连接客户端操作

思路:

(1)可以显示一下 因为已经接收到了客户端 ,那这里打印一次客户端已上线

(2)Socket返回套接字输入流,返回套接字输出流,因为TCP是流式操作的

(3)处理肯定不是一个请求和响应所以需要循环操作,之后读取请求,判断请求数据是否存在,不存在那连接也就断开了 ,如果存在跳过即可,进行数据输入

(4)根据请求构造响应

(5)这里返回响应结果想要是一个字符串,但是接收来的是字节流 需要 稍作转换,转换成字符串

 (6)服务器打印

 以下是附上processConnection代码

 private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线! \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //基于上述socket 对象和 客户端 进行通信
        //以下使用 try() 是java语法 try with resources
        try (InputStream inputStream=clientSocket.getInputStream();
             OutputStream outputStream=clientSocket.getOutputStream()){
            //由于要处理多个请求和 响应 也是使用 循环进行的
            while(true){
                //1、读取请求
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    System.out.printf("[%s:%d]客户端下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //注意 !此处使用next 是一直 读取到换行符 或者 空格 或者 其他空白字符tab 但是 最终返回结果里不包含前面提到的这几个类别
                String request=scanner.next();
                //2、根据请求构造响应
                String response=process(request);
                //3、返回响应结果
                //OutputStream 没有write String功能 这里需要转换 可以把String 里的字节数组转换出来 进行写入
                //也可以用字符流来转化一下
                PrintWriter printWriter=new PrintWriter(outputStream);
                //此处使用 println来写入 让结果带有一个 \n 换行 方便对端来接收解析
                printWriter.println(response);
                //前面IO文件操作 中知道在字符写文件的时候 是需要刷新的所以这里也会进行
                printWriter.flush();

                System.out.printf("[%s:%d] req:%s ,resp :%s \n",clientSocket.getInetAddress().toString(),clientSocket.getPort()
                ,request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                //此处需要处理一下
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
 public String process(String request) { //这里也只是走个过程,并没有真的对请求进行处理
        return request;
    }

 main方法操作:

 3.3、TCP版本的客户端

提示:自定义一个TCPEchoClient类 以下TCP客户端代码都在该类中 

如果友友们,从前面看到这里了,基本不管是创建服务器还是创建一个客户端都是创建Socket对象,TCP客户端代码就没有TCP服务器代码麻烦了。

(1)创建Socket对象 写一个构造方法接收服务器的IP地址和 端口号

//定义一个Socket对象用来接收 IP地址和 端口号
    private Socket socket=null;

    public TcpEchoClient(String serverIp ,int serverPort) throws IOException {
        //Socket构造方法 能够识别点分十进制格式的 IP地址 比DatagramPacket更方便
        //new 这个对象的同时 就会进行TCP连接操作
        socket=new Socket(serverIp,serverPort);
    }

(2)写一个客户端启动start方法

客户端:用户写入数据、接收套接字输入流和输出流、连接结束的判断 、数据发送、接收数据

此处就不在详细解释了,基本和以上代码相似,代码配有详细的注释

public void start(){
        System.out.println("客户端启动");
        Scanner scanner=new Scanner(System.in);
        //接收套接字的输入流 和 输出流
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            while(true){
                System.out.println(">");
                //1. 先从键盘上读取用户数据内容
                String request=scanner.next();
                //判定输入一个算是解除连接  我们这里给了一个exit 表示结束连接 就相当于是 挂电话的按键
                if(request.equals("exit")){
                    System.out.println("goodbye");
                    break;
                }
                //2 把读到的内容构造请求发送给服务器  这里和TCP服务器是一样的 都需要进行转换将字节转化为字符串
                PrintWriter printWriter=new PrintWriter(outputStream);
                //发送给服务器 printWriter 的 println方法本身就会起到发送的作用
                printWriter.println(request);
                //此处需要刷新缓冲区 确保数据是发出去了,
                printWriter.flush();
                //3 服务器的响应    Scanner本身就参数就是字节输入流参数
                Scanner respScanner=new Scanner(inputStream);
                //进行读取
                String response=respScanner.next();
                //4 把响应内容显示到界面上
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

代码执行作以下分析:

以上代码是写完了,但是这里还有几个想要提醒要点

(1)疑惑:其中使用了printWriter类来转换字节流变成字符串是否还是同一个文件描述符表,OutputStream相当于一个文件描述符(socket文件)通过OutputStream就可以往这个 文件描述符表中写入数据,但是当前的OutputStream自身方法不方便 写字符串需要进行转换 ,PrintWriter对象来表示(对应的是同一个文件描述符表)

(2)以上能看发送是通过PrintWriter类中println方法进行发送的,细节问:如果不用println,直接用print发送行不行???

答案:不行(图解)

 3.4、TCP连接操作

 连接之后就发现问题了,不能多个客户端进行连接,如何解决呢?答案:多线程

3.4.1多线程TCP服务器

以前面的TCP服务器作为地基 这里有start方法内部需要稍作修改即可

 public void start() throws IOException {
        System.out.println("启动服务器");
        while(true){
            //使用这个 clientSocket 和 具体的的客户端进行交流
            //具体进行服务器  这里用作监听 客户端是否上线
            Socket clientSocket =serverSocket.accept();
            //如果上线  accept方法 阻塞结束 进行连接操作
            Thread thread=new Thread(()->{
                processConnection(clientSocket);
            });
            thread.start();
        }
    }

为什么多线程操作加在这里不用过多的解释,那多线程为什么不包含以下的代码呢

Socket clientSocket =serverSocket.accept();

 这里在while循环中,所以每次多线程执行并且在当前while循环中结束,再进行下一次while循环

 多线程已经能解决问题了,但这里还不打算演示,因为我们的客户端最多也就只能开几个,确实带的动,C10K(1w客户端)问题解决了,但是如果更多呢C10M(1kw客户端)问题呢,创建或者销毁这么多线程也是要有开销的,多线程提到开销问题优化措施就会想到线程池。

3.4.2、线程池TCP服务器

修改多线程修改代码位置相同

public void start() throws IOException {
        System.out.println("启动服务器");
        //创建线程池  这里创建的不是固定数量线程的池子, 是根据工作需要创建的线程池
        ExecutorService service=Executors.newCachedThreadPool();
        while(true){
            //使用这个 clientSocket 和 具体的的客户端进行交流
            //具体进行服务器  这里用作监听 客户端是否上线
            Socket clientSocket =serverSocket.accept();
            //如果上线  accept方法 阻塞结束 进行连接操作
            //提交任务
           service.submit(()->{
               processConnection(clientSocket);
           });
        }
    }

 这里就没有什么过多的解释了,如果对线程池不是很理解的友友(可以看下这篇博客)

现在演示,使用多线程之后,客户端有怎么样的结果

 注 :TCP版本服务器和客户端没有写相关业务,友友们可以模仿这UDP翻译业务也给TCP写一个,基本是一样的写法,这里再TCP版本中就不在重复进行了。

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

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

相关文章

【项目实战】一文入门项目中Lombok的常用注解

一、Lombok介绍 1.1 Lombok是什么&#xff1f; 一个Java库&#xff0c;用于简化Java代码。 Lombok是一个非常神奇的 java 类库&#xff0c;会利用注解自动生成 java Bean 中烦人的 Getter、Setting&#xff0c;还能自动生成 logger、ToString、HashCode、Builder 等 java特色…

【GD32F427开发板试用】开发一款网络音乐播放器

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;守勤 资源介绍 非常荣幸能够参与到这次GD32F427开发板试用的活动中来&#xff0c;开发板的设计非常简洁&#xff0c;板载了一颗GD32F103C8T6和…

Python中的递归及案例演示

目录 一.什么是递归 二.案例 递归找文件 步骤 os模块中的三个方法 演示 最终代码 三.总结 一.什么是递归 递归在编程中是一种非常重要的算法 递归:即方法(函数)自己调用自己的一种特殊编程写法 如&#xff1a; 函数调用自己&#xff0c;即称之为递归调用。 二.案例 递…

C++ 引用! 他是坤坤也是鸡哥

&#x1f451;专栏内容&#xff1a;C学习笔记⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;日拱一卒&#xff0c;功不唐捐 目录一、前言二、引用1、引用的概念2、引用的声明3、引用的特性Ⅰ、 引用在定义时必须初始化Ⅱ、 一个变量可以有多个引用Ⅲ、引…

深度学习PyTorch 之 DNN-多分类

前面讲了深度学习&PyTorch 之 DNN-二分类&#xff0c;本节讲一下DNN多分类相关的内容&#xff0c;这里分三步进行演示 结构化数据 我们还是以iris数据集为例&#xff0c;因为这个与前面的流程完全一样&#xff0c;只有在模型定义时有些区别 损失函数不一样 二分类时用的损…

Pollard Rho算法

生日悖论 假设一年有nnn天&#xff0c;房间中有kkk人&#xff0c;每个人的生日在这nnn天中&#xff0c;服从均匀分布&#xff0c;两个人的生日相互独立 问至少要有多少人&#xff0c;才能使其中两个人生日相同的概率达到ppp 解&#xff1a;考虑k≤nk\le nk≤n 设kkk个人生日互…

Spring框架介绍及使用

文章目录1.概述1.1 Spring是什么1.2 Spring 的优势1.3 spring 的体系结构2. IoC 的概念和作用2.1 什么是程序的耦合2.2 IoC容器3. AOP的概念和作用超链接&#xff1a; Spring重点内容学习资料1.概述 1.1 Spring是什么 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源…

使用docker-compose搭建Prometheus+Grafana监控系统

一、角色分配 Prometheus 采集数据Grafana 用于图表展示redis_exporter 用于收集redis的metricsnode-exporter 用于收集操作系统和硬件信息的metricscadvisor 用于收集docker的相关metrics 二、安装Docker 可以参考&#xff1a;https://ximeneschen.blog.csdn.net/article/d…

JVM调优实战:to-space exhausted Evacuation Failure

一次线上dubbo问题的定位&#xff0c;进行JVM调优实战。问题线上dubbo接口provider抛出异常&#xff1a;org.apache.dubbo.rpc.RpcException: Failfast invoke providers ... RandomLoadBalance select from all providers ... use dubbo version 2.7.16, but no luck to perfo…

vulnhub DC系列 DC-8

总结&#xff1a;exim4提权 目录 下载地址 漏洞分析 信息收集 网站爆破 后台webshell 提权 下载地址 DC-8.zip (Size: 379 MB)Download: http://www.five86.com/downloads/DC-8.zipDownload (Mirror): https://download.vulnhub.com/dc/DC-8.zip使用方法:解压后&#xff…

Cosmos 基础(二)-- Ignite CLI

官网 DOC GitHub 你的项目值得拥有自己的区块链。 Ignite使开发、增长和启动区块链项目比以往任何时候都更快。 Ignite CLI是一个一体化平台&#xff0c;可以在主权和安全的区块链上构建、启动和维护任何加密应用程序 Install Ignite 一、安装 你可以在基于web的Gitpod…

23种设计模式(七)——桥接模式【单一职责】

文章目录 意图什么时候使用桥接真实世界类比桥接模式的实现桥接模式的优缺点亦称:Bridge 意图 桥接模式是将抽象部分与实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式。 什么时候使用桥接 1、如果一个…

详解MySQL数据库索引实现机制 - B树和B+树

详解MySQL数据库索引实现机制 - B树和B树1.索引的出现2.hash算法的缺点3.二叉排序树BST4.平衡二叉树AVL5.红黑树6.B树诞生了7.B树1.索引的出现 索引是一种用于快速查询和检索数据的数据结构&#xff0c;其本质可以看成是一种排序好的数据结构。 索引的作用就相当于书的目录。…

(Netty)Handler Pipeline

Handler & Pipeline ChannelHandler 用来处理 Channel 上的各种事件&#xff0c;分为入站、出站两种。所有 ChannelHandler 被连成一串&#xff0c;就是 Pipeline 入站处理器通常是 ChannelInboundHandlerAdapter 的子类&#xff0c;主要用来读取客户端数据&#xff0c;写…

【嵌入式处理器】CPU、MPU、MCU、DSP、SoC、SiP的联系与区别

1、CPU(Central Processing Unit) CPU(Central Processing Unit)&#xff0c;是一台计算机的运算核心和控制核心。CPU由运算器、控制器和寄存器及实现它们之间联系的数据、控制及状态的总线构成。众所周知的三级流水线&#xff1a;取址、译码、执行的对象就是CPU&#xff0c;差…

重学Android之View——TabLayoutMediator解析

重学Android之View——TabLayoutMediator解析 1.前言 在使用TabLayoutViewPager2Fragment的时候&#xff0c;查询别人的使用例子&#xff0c;看到了 TabLayoutMediator这个类&#xff0c;撰写此文&#xff0c;仅当学习思考&#xff0c;本文是在引用material:1.7.0的版本基础…

记2022年秋招经历

自我介绍求职体验求职心得 一、自我介绍 学历普通本科&#xff0c;专业是网络工程&#xff0c;在校期间学习主要的是计算机体系方面的知识&#xff0c;根据课程&#xff0c;自学过前端、后端等内容。包括前端三板斧(htmlcssjs)、常用的前端框架(bootstarp/Vue等&#xff09;&am…

Android项目接入React Native方案

本篇文章主要介绍在现有的Android项目中接入React Native的接入过程&#xff0c;分析接入过程中的一些问题和解决方案&#xff0c;接入RN的平台为Android&#xff0c;开发环境为Mac&#xff0c;开发工具为Android Studio。 一、环境配置 1、Android配置 因为是现有的Android项…

Vue实现DOM元素拖放互换位置

一、拖放和释放HTML 拖放接口使得 web 应用能够在网页中拖放文件。这里将介绍了 web 应用如何接受从底层平台的文件管理器拖动DOM的操作。拖放的主要步骤是为 drop 事件定义一个释放区(释放文件的目标元素) 和为dragover事件定义一个事件处理程序。触发 drop 事件的目标元素需要…

day20IO流

1.字符流 1.1为什么会出现字符流【理解】 字符流的介绍 由于字节流操作中文不是特别的方便&#xff0c;所以Java就提供字符流 字符流 字节流 编码表 中文的字节存储方式 用字节流复制文本文件时&#xff0c;文本文件也会有中文&#xff0c;但是没有问题&#xff0c;原因是最…