计算机网络 - 网络通信 (TCP与UDP)

news2025/2/26 22:54:12

前言

本篇通过了解套接字,TCP与UDP协议的特点,使用UDP的api套接字与TCP的api套接字进行回显程序的网络通信,如有错误,请在评论区指正,让我们一起交流,共同进步!


文章目录

    • 前言
    • 1. 认识网络通信需要的套接字 - socket
    • 2. UDP的api
      • 2.1 DatagramSocket
      • 2.2 DatagramPacket
      • 2.3 根据DatagramSocket 与 DatagramPacket 模拟网络通信
      • ==1.模拟实现UDP回显服务器==
      • ==2.模拟实现客户端==
    • 3. TCP的api
      • 3.1 ServerSocet
      • 3.2 Socket
      • 3.3 模拟实现TCP回显程序
      • 3.3.2 模拟实现客户端
    • 总结

本文开始

1. 认识网络通信需要的套接字 - socket

1.1 认识套接字
套接字:应用层调用传输层,操作系统内核给应用层提供一组用来网络编程的api,这就称为 socket api = 套接字api;

针对TCP与UDP协议,这里主要认识关于UDP的api 与 TCP的api 来用于网络通信;
认识api先认识 TCP 与 UDP 的特点吧!

1.2 TCP 与 UDP的主要特点
UDP:

① 无连接:使用UDP通信,双方不需要保存对端(客户端与服务器)的信息;
例如:发短信,不需要记录对方的电话,对方也不需要记录你的电话 (不用建立连接),直接根据电话号直接发送信息;
② 不可靠传输:一方发送消息,不需要知道另一方是否接收到了信息;
③ 面向数据报:以一个UDP数据报为基本单位读写数据;- 有限制;
④ 全双工:一条路径双向通信;
【注】有无连接:通信双方是否记录对方的信息;
有连接:通信双方需要记录对方的信息;
无连接:通信双方不需要记录对方的信息;

TCP:

① 有连接:使用TCP通信,双方需要保存对端的信息;
例如:打电话,一方打电话,另一方需要接通电话 (建立连接),双方才能通信;
② 可靠传输:一方发送信息,尽量保证信息发送到了另一方(但也不能保证一定成功,只是尽全力);
③ 面向字节流:以一个字节为传输基本单位 - 读写数据比较灵活
④ 全双工:双向通信;
【注】半双工:在一条路径上,A方向B方发送消息,B只能等待A发完消息后才能向A发送信息;
全双工:A向B发送消息的同时,B也可以向A发送消息;

2. UDP的api

认识socket对象:
socket 是系统中一个特殊的文件进行网络通信,需要socket文件对象,通过socket文件对象,间接操作网卡;

2.1 DatagramSocket

Datagram: 数据报;Socket: socket对象;

在这里插入图片描述
构造方法:
在这里插入图片描述

【注】socket对象可以被客户端 与 服务器使用;服务器使用socket需要指定关联一个端口号 - 端口号不变才能方便客户端找到服务器;客户端使用socket不需要手动指定,系统会自动分配空闲的端口号;

2.2 DatagramPacket

DatagramPacket: udp数据报对象;

在这里插入图片描述
构造方法
在这里插入图片描述

2.3 根据DatagramSocket 与 DatagramPacket 模拟网络通信

回显程序:自己给自己发送并接收信息;

1.模拟实现UDP回显服务器

服务器的核心工作:
① 读取客户端请求并解析
② 根据请求计算响应
③ 把响应写回到客户端

首先构造socket对象,从而间接操作网卡进行读取;
再构造带有一个参数的构造方法,给服务器分配端口;

	private DatagramSocket socket = null;
    //绑定端口号不一定会成功;- 端口号可能被别的进程占用
    //同一主机,同一时刻,一个端口只能被一个进程所占用
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

服务器主要逻辑过程:
① 读取客户端请求:使用receive()方法, 需要构造requestPacket数据报把数据读取到数据报中;
② 知道客户端输入的是字符,为方便操作将数据报(字节数组)构造为字符串;
③ 根据请求计算具体响应 - 根据具体情况修改;
④ 将响应发送回客户端,根据响应是字符串,构造数据报此时需要指定客户端的IP和端口;

public void start() throws IOException {
        System.out.println("服务器启动!");
        //服务器执行不可能只执行一个请求,需要执行多个请求就用到了循环
        while (true) {
            //1.读取客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[5000],5000);
            //receive时输出型参数:在其中传入空的packet对象,然后receive方法内部就会把参数packet填充;
            // -》从网卡读取内容,写的数据报中;
            socket.receive(requestPacket);//服务器先启动,如果此时没有客户端请求,就会阻塞等待,等待客户端发送数据过来;
            //解析 -> 此处为了方便后续操作,拿字节数组构造成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //getLength : 获取真实长度;
            //2.根据请求计算响应
            String response = process(request);
            //3.把响应写回客户端 - 写回网卡
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes(StandardCharsets.UTF_8).length,
                    //requestPacket是客户端得到的,getSocketAddress得到的就是客户端的IP和端口
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印日志
            System.out.printf("[%s : %d] request: %s, response: %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }
    //回显服务器,写什么,就返回什么;
    // 以后有其他服务器,可以根据具体请求重新构造响应;
    private String process(String request) {
        return request;
    }

【注】receive时输出型参数:在其中传入空的packet对象,然后receive方法内部就会把参数packet填充;

服务器总代码:

public class UdpEchoServer {
    private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            //1.读取客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[5000],5000);
            socket.receive(requestPacket);
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //2.根据请求计算响应
            String response = process(request);
            //3.把响应写回客户端 - 写回网卡
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes(StandardCharsets.UTF_8).length,
            requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印
            System.out.printf("[%s : %d] request: %s, response: %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }
    private String process(String request) {
        return request;
    }

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

2.模拟实现客户端

首先构造socket对象,方便操作读取网卡;
再构造带两个参数的构造方法:指定服务器的IP和端口,让客户端容易找到服务器;

public class UdpEchoClient {
	private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    //客户端启动需要知道服务器的位置,根据服务器的IP和端口
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //客户端不用指定端口,会自动分配空闲端口;
        socket = new DatagramSocket();
        //为了方便之后使用
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
}

客户端执行逻辑:
① 等待客户端输入请求;
② 把请求发送给服务器,需构造数据报并指定服务器IP和端口;
③ 客户端接收响应,构造数据报使用receive()接收;
④ 打印响应结果,再将数据报转换为字符串
代码如下:

     public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        //多次交互
        while (true) {
            System.out.print(" -> ");
            //1.等待控制台输入
            String request = scanner.next();
            //2.把请求发送到服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
            //通过静态发送InetAddress设置IP地址;        
            InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);
            //3.客户端接收响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[5000],5000);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            //4.打印日志
            System.out.printf("request: %s, response: %s\n", request,response);
        }
    }

【注】构造数据报DatagramPacket requestPacket:请求的起始0位置和请求的最后长度length; 需要服务器的IP和端口;
区别:SocketAddress:包含IP和端口;InetSocketAddress设置 IP 再单独设置端口;

客户端总代码:

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    //客户端启动需要知道服务器的位置,根据服务器的IP和端口
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //客户端不用指定端口,会自动分配空闲端口;
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        //多次交互
        while (true) {
            System.out.print(" -> ");
            //1.等待控制台输入
            String request = scanner.next();
            //2.把请求发送到服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);
            //3.客户端接收响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[5000],5000);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            //4.打印日志
            System.out.printf("request: %s, response: %s\n", request,response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",8080);
        udpEchoClient.start();
    }
}

客户端与服务器的网络通信过程:
① 服务器先启动,执行到receive方法后就进行阻塞,等待客户端传输数据 / 请求;
② 客户端运行,等待控制台输入数据并读取,包装成数据报后发送send() 给服务器;
③ 客户端发送数据报后,继续向下执行,到receive方法会阻塞等待服务器返回响应;
     在客户端发送请求后的后的同时,服务器会读取客户端的请求,根据请求生成响应,把响应包装成数据报再次send发送给客户端,并打印日志;
④客户端收到服务器的响应,就会解除receive的阻塞,把响应转换为字符串进行打印;
      此时服务器会进入下一轮循环,再次等待新的客户端请求;
⑤ 客户端打印完,进入下一轮,再次等待控制台输入新的数据请求;
【注】本机IP地址:127.0.0.1; - 自己与自己发信息;

3. TCP的api

3.1 ServerSocet

ServerSocket: 服务器使用的
ServerSocket构造时需要让服务器绑定一个指定的端口,方便客户端能够找到;

在这里插入图片描述
构造方法:
在这里插入图片描述

3.2 Socket

Socket: 服务器 和 客户端都可以使用

在这里插入图片描述
构造方法
在这里插入图片描述

3.3 模拟实现TCP回显程序

3.3.1 模拟实现回显服务器
了解InputStream, OutputStream操作字节流对象;
InputStream: 读数据,相当于从网卡读数据;
OutputSteam: 写数据,相当于从网卡发送数据;

首先类TcpEchoServer, 类中创建ServerSocket变量,方便以后操作网卡读取数据;
构造一个参数的构造方法,构造serverSocket对象并指定端口号;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
    	//与之前的UDP的DatagramSocket中的socket一样,需要绑定端口
        serverSocket = new ServerSocket(port);
    }
}

然后服务器中的主逻辑:
① 先调用serverSocket的accept方法,等待与客户端建立连接

在这里插入图片描述

② 建立连接后,执行客户端连接的方法processConnectin();

核心逻辑代码:

public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            //监听,看绑定的端口是否有客户端连接,如果连接accept就会马上返回Socket对象
            Socket clientSocket = serverSocket.accept();
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
            t.start();
        }

为什么使用线程呢?
服务器启动后,如果不使用线程,当一个客户端占用processConnection(clientSocket)方法,有其他客户端想要再次使用processConnection(clientSocket)方法,就需要一直等待;使用多线程,来一个客户端就创建一个新线程来执行processConnection(clientSocket)方法,就可以多线程使用了;

优化为线程池:
线程的频繁创建和销毁也会带来资源销毁,这里可以优化为线程池;将线程放入线程池,减少线程的创建和销毁;

public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true) {
            //监听,看绑定的端口是否有客户端连接,如果连接accept就会马上返回Socket对象
            Socket clientSocket = serverSocket.accept();
           executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

processConnection(clientSocket)方法具体执行内容:
代码逻辑:
① 读取数据需要对网卡的操作,就用到了InputStream,OutputStream; - try() 中允许写多个流对象;
② 上述字节流,不能清楚读取到哪是一个请求;这里约定服务器是字符串请求,请求之间用\n分隔,所以包装为字符流Scanner , PrintWrite 方便读写数据; - 根据具体情况可更改;
③ 执行客户端请求,与UDP相似,但这里需要判断客户端是否断开连接使用hasNext()方法;
【注】 根据请求计算完响应,发送响应时也应该带上 \n 让客户端分清楚请求 - 使用println加 \n ;(客户端也一样)
clientSocket 是文件,不用了需要关闭;而ServerSocket不需要,它的生命周期长;(循环创建clientSocket,有一个客户端就创建一个,为防止文件资源泄露,需要close()关闭一下文件;)

代码如下:

private void processConnection(Socket clientSocket) {
        System.out.printf("客户端上线![%s : %d]\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStrem = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //为了后续操作,字节流包装为字符流 - 可根据具体情况修改;
            Scanner scanner = new Scanner(inputStrem);
            PrintWriter printWriter = new PrintWriter(outputStream);
            //执行具体请求
            while (true) {
                //1.读取客户端请求
                //判断客户端是否关闭了
                if(!scanner.hasNext()) {
                    System.out.printf("客户端下线![%s : %d]\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //scanner.next读取换行结束,读取一段请求;
                String request = scanner.next();
              // 读到空白符结束;空白符:\n,空格,制表符;
                //2.根据请求做出响应
                String response = process(request);
                //3.返回响应,响应中加上\n,上述读取一段请求时next不读取\n,这里发送时需要加上;
                printWriter.println(response);
                printWriter.flush();//刷新,将缓冲区数据立即发送
                //4.打印日志
                System.out.printf("[%s : %d] request: %s, response: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

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

为什么InputStream,OutputStream;写到try括号里?
放在try()中,InputStream,OutputStream最后会自动关闭,不用手动close;

为什么需要printWriter.flush() ?
发送的数据,先会写入内存的发送缓冲区,等到缓冲区满了,才会写入网卡;这里使用flush(), 使数据立即写入网卡,不用等待缓冲区满;- 客户端与服务器相同,都需要手动flush()一下;

3.3.2 模拟实现客户端

首先创建TcpEchoClient类,类中创建Socekt操作网卡,创建两个参数的构造方法,让客户端能够找到服务器,与服务器来连接;

public class TcpEchoClient {
    private Socket socket = null;
    public TcpEchoClient(String rerverIP, int port) throws IOException {
        socket = new Socket(rerverIP,port);
    }
}

核心逻辑代码:
与UDP类似;

public void start() {
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            //包装为字符流
            PrintWriter printWriter = new PrintWriter(outputStream);
            Scanner scFromSocket = new Scanner(inputStream);//读取网卡请求
            while (true) {
                //1.等待控制台输入
                System.out.println(" --> ");
                String request = scanner.next();//这里next以分隔符\n来分隔每段请求
                //2.发送请求给服务器
                printWriter.println(request);
                printWriter.flush();
                //3.接收服务器响应
                String response = scFromSocket.next();//读取字符流读取字符
                //4.打印日志
                System.out.printf("[%s : %d] request: %s, response: %s\n",socket.getInetAddress().toString(),
                        socket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

TCP客户端-服务器执行过程:
① 服务器先启动,阻塞在accept(),等待客户端连接
② 客户端启动,调用构造方法与服务器建立连接,此时服务器中accept()返回Socket对象;
③ 服务器执行processConnection方法,阻塞到读取客户端请求;
而客户端在调用构造方法后一直往下执行,阻塞等待用户输入,用户输入后读取数据;
④ 客户端读取用户数据发送给服务器,执行到要读取客户端响应后阻塞等待服务器返回响应;
⑤ 服务器接收请求后,读取请求,计算响应,并返回响应;
⑥ 服务器发送响应后重写循环等待下一轮客户端连接;
客户端收到响应,打印结果,再次循环,等待控制台输入;

TCP 客户端总代码:

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("服务器启动!");
        ExecutorService executorService = Executors.newCachedThreadPool();
        while (true) {
            //监听,看绑定的端口是否有客户端连接,如果连接accept就会马上返回Socket对象
            Socket clientSocket = serverSocket.accept();
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("客户端上线![%s : %d]\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStrem = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //为了后续操作
            Scanner scanner = new Scanner(inputStrem);
            PrintWriter printWriter = new PrintWriter(outputStream);
            //执行具体请求
            while (true) {
                //1.读取客户端请求
                //判断客户端是否关闭了
                if(!scanner.hasNext()) {
                    System.out.printf("客户端下线![%s : %d]\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                String request = scanner.next();
                //2.根据请求做出响应
                String response = process(request);
                //3.返回响应
                printWriter.println(response);
                printWriter.flush();
                //4.打印日志
                System.out.printf("[%s : %d] request: %s, response: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);

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

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

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

总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

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

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

相关文章

JVM、JVM中的垃圾回收、类加载、IoC和DI

一、JVM 1、概念 JVM:Java Virtual Machine 的简称,意为 Java虚拟机,可以运行Java代码,是整个Java实现跨平台的最核心的部分;所有的Java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行&…

2022-ISCC信息安全与对抗竞赛wp-misc(详解,有脚本( •̀ ω •́ )y)

前言 没想到不知不觉一年时间就这样过去了,又到了一年一度的ISCC信息对抗大赛,不知道去年打比赛的小伙伴今年还能不能再碰到,期待与君再相见( •̀ ω •́ )y 所以今天就把去年的题目再复现一遍供师傅们参考 嘻嘻 目录 misc &#xff08…

2023MathorCup数模B题思路数据代码论文【全网最全分享】

文章目录赛题思路赛题详情参赛建议(个人见解)选择队友及任务分配问题(重要程度:5星)2023MathorCup数模B题思路数据论文代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,…

STM-32:I2C外设总线—硬件I2C读写MPU6050

目录一、I2C外设简介二、I2C框图三、I2C基本结构四、主机发送五、主机接收六、I2C的中断请求七、软件/硬件波形对比八、应用实例:硬件I2C读写MPU60508.1接线图8.2程序代码一、I2C外设简介 STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、…

Blender安装最新版本

目录1. Blender下载1.1 Blender硬件要求1.2 下载Blender1.3 或者下载LTS版本2. 安装向导2.1 在Windows上安装2.1.1安装msi文件(这里演示案例)2.1.2 点击“Next”2.1.3 勾选,点击“Next”2.1.4 点击“Next”2.1.5 点击“Install”2.1.6 安装进度2.1.7 点击“Finish”…

来了,简单宣告下 Compose for iOS Alpha 正式发布

来了来了,盼星星盼月亮,广大 Compose 开发者期待许久的 Jetpack Compose Multiplatform for iOS 近期终于正式发布了 Alpha 支持,其实在此之前,我在 《一文快速带你了解 KMM 、 Compose 和 Flutter 的现状》 等文章里说了很多次 C…

十三款MySQL可视化管理工具

文章目录一、DBeaver二、DataGrip三、phpMyAdmin四、MySQLDumper五、Navicat六、MySQL GUI Tools七、MySQL ODBC Connector八、MySQL Workbench九、SQLyog十、MySQL-Front十一、dbForge Studio for MySQL十二、HeidiSQL十三、Beekeeper StudioMySQL 的管理维护工具非常多&#…

【C++ 二】选择结构、循环结构、跳转语句

选择结构、循环结构、跳转语句 文章目录选择结构、循环结构、跳转语句前言1 选择结构1.1 if语句1.2 三目运算符1.3 switch 语句2 循环结构2.1 while 循环语句2.2 do...while 循环语句2.3 for 循环语句2.4 嵌套循环3 跳转语句3.1 break 语句3.2 continue 语句3.3 goto语句总结前…

Unity | Video Player的使用方法和原理

讲解逻辑为Unity | 基础逻辑_菌菌巧乐兹的博客-CSDN博客 一、把素材仓库里的视频放进Game界面 1.我们需要在素材仓库中放一个视频(其他文件夹不用管,我只是懒得删了) 2.在Unity里建一个能接收视频的地方VideoPlayer 备注:视频和…

【Python基础入门学习】Python背景知识及介绍

一、背景知识 主流编程语言:java/python/c(c) Python主要应用领域:人工智能、大数据 云计算领域的openstack 框架就是Python写的 测试发展方向:测开、安全、性能 1.1 发展过程 机器语言(二进制)-- 汇编语言 – 高级汇…

招募妙记多 Mojidoc 体验官| 我们准备了诚意大礼,就等你来

在以往的推文和妙记多 Mojidoc 的官方社群里,我们分享、见证了很多妙记多 Mojidoc 用户在产品使用过程中的故事。 我们欣喜于大家对 妙记多 Mojidoc 的信任,也惊喜于在这个过程中,大家对于产品的优化和升级,不遗余力地提出建议和…

IDEA虚拟机参数配置【自我总结】

idea内存溢出问题 最近自己在跑一个大一点的微服务项目时候,发生了这样的一个现象,就是启动idea的时候,首先发现电脑的CPU小风扇急速的飞转,电脑的运行内存也快要飙升到100%了,而且最重要的是光是加载项目就加载了几分钟啊(我当时就炸锅锅)像下图一样,一直加载一直加载 想了想,…

一天吃透计算机网络八股文

网络分层结构 计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。最全面的Java面试网站 五层模型:应用层、传输层、网络层、数据链路层、物理层。 应用层:为应用程序提供交互服务…

MySQL:开窗函数

当查询条件需要用到复杂子查询时,聚合函数操作起来非常麻烦,因此使用开窗函数能够轻松实现。 注意:在Oracle中称为分析函数。 在MySQL中称为开窗函数,使用于MySQL8.0以上版本,sql sever、hive、Oracle等。 1 开窗函数…

Redis为什么能抗住10万并发?揭秘性能优越的背后原因

1. Redis简介 Redis是一个开源的,基于内存的,高性能的键值型数据库。它支持多种数据结构,包含五种基本类型 String(字符串)、Hash(哈希)、List(列表)、Set(集…

Win10的两个实用技巧系列之华硕电脑设置面部识别的技巧、删除背景图片的方法

Win10系统的华硕电脑怎么使用人脸解锁? 华硕电脑设置面部识别的技巧 Win10系统的华硕电脑怎么使用人脸解锁?华硕电脑想要添加面部识别,方便人脸解锁,下面我们就来看看华硕电脑设置面部识别的技巧 有些使用Win10系统的华硕电脑的用户&#…

X509证书以及相关java常用接口

二、X509证书 X.509证书是一种数字证书标准,用于验证在计算机网络中的身份认证。它们是由权威机构(例如CA)发行,包含有关证书持有者身份信息的数字签名。X.509证书通常用于SSL / TLS协议,以确保客户端和服务器之间的安…

Word转PDF:一键转换,快速高效

在现代的工作和学习中,我们经常需要将Word文档转换成PDF文件以便于分享和保留格式。虽然Word软件自身也可以保存为PDF格式,但在某些情况下,我们需要一个更快捷、高效的转换工具来帮助我们完成任务。下面将介绍一款快速高效的Word转PDF在线转换…

机器学习与深度学习——通过SVM线性支持向量机分类鸢尾花数据集iris求出错误率并可视化

线性支持向量机 先来看一下什么叫数据近似线性可分,如下图所示,蓝色圆点和红色圆点分别代表正类和负类,显然我们不能找到一个线性的分离超平面将这两类完全正确的分开;但是如果将数据中的某些特异点(黑色箭头指向的点)去除之后&a…

华为ensp配置实验大全(免费持续更新)

点开一篇文章 ,“分享”要收费。 点开一篇文章, "大全"但就两个实验。 点开一篇文章, “详细"但全截图。 我忽略了最重要的东西"产品说明书" 产品说明书优势 "规范" "详细" 此文寻找官方手册中…