【JavaEE】网络编程之TCP套接字

news2024/11/26 13:46:40

目录

1、TCP套接字

1.1、ServerSocket  常用API

1.2、Socket 常用API

2、基于TCP套接字实现一个TCP回显服务器

2.1、服务器端代码

2.2、客户端代码 

 2.3、解决服务器不能同时和多个客户端建立链接的问题 

3、基于TCP socket 写一个简单的单词翻译服务器


1、TCP套接字

TCP和UDP相比有很大的不同,TCP需要进行网络通信,首先需要建立链接,成功之后客户端和服务器之间才能进行通信,TCP进行网络编程的方式和二进制文件的读写操作比较类似,都是以字节为单位流式传输。

Java对TCP的套接字提供了两个类分别为ServerSocket和Socket。

1.1、ServerSocket  常用API

这个ServerSocket类是给服务器使用的。

✨ServerSocker构造方法

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

✨ServerSocket成员方法

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端链接后,返回一个服务器端Socket对象,并基于该Socket建立与客户端的链接,否则阻塞等待
void close()关闭套接字

1.2、Socket 常用API

这个Socket类服务器和客户端都可以使用。

✨Socket构造方法

方法签名方法说明
Socket(String host,int port)创建一个客户端流套接字Socket,并于对应IP的主机上,对应端口的进程建立链接

这个构造方法的参数表示的是服务器的IP和端口,应为TCP是有链接的,所以在客户端new Socket 对象的时候,就会尝试和指定的IP和端口的目标建立链接了。 

✨Socket成员方法

方法签名方法说明
InetAddress  getInetAddress()返回套接字所链接的地址
InputStream  getInputStream()返回此套接字的输入流
OutputStream getOutStream()返回此套接字的输出流
close()关闭套接字

之前说过TCP是面向字节流进行数据传输的,通过Socket对象就可以拿到里面所包含的字节流对象,拿到字节流对象之后就可以进行数据传输了,从InputStream这里读数据,就相当于从网卡接收,往OutputStream这里写数据,就相当于从网卡发送。


2、基于TCP套接字实现一个TCP回显服务器

2.1、服务器端代码

  1. 创建ServerSocket对象,并指定服务器端口号。
  2. 启动服务器,使用accept方法和客户端建立链接,如果没有客户端链接上服务器,那么sccept方法就会阻塞等待,直到和客户端和服务器建立链接。
  3. 读取客户端发来的请求。
  4. 处理客户端请求,计算响应
  5. 将响应返回给客户端。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
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){
            //和客户端建立链接
                //这里的clientSocket是服务器上的socket,我们只是通过这个socket和客户端
//进行通信的,所以我们给这个socket起了一个名字叫做clientSocket ,并不是说这个clientSocket就对应到客户端的网卡。
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }
    //通过这个方法来处理一个链接
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        //try()这种写法,()中允许写多个流对象,使用;来分割。
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用scanner和printWriter将inputStream和outputStream进行包裹,这样可以将字符流转换为字节流
            Scanner scanner = new Scanner(inputStream);
            while(true){

                //1、读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾了(对端关闭了),打印客户端的IP地址和端口号
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request = scanner.next();

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

                //3、把响应返回给客户端.不要忘记,响应这里也要带上换行的
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //这里的clientSocket文件只是给一个链接提供服务的文件,所以客户端和服务器断开链接
//之后就需要释放clientSocket文件资源。链接有很多,时刻都会建立新连接,时刻都会有旧链接断开。
//serverSocket对象文件的生命周期和服务器进程的生命周期一样,所以不需要显示的进行文件资源释放。
            clientSocket.close();
        }
    }

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

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

✨下面对这个服务器端的代码进行逐步解析

1️⃣上述代码中创建了一个serverSocket对象,但是在start()方法中又创建了一个clientSocket对象

 public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            //和客户端建立链接
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }

这里accept()方法的作用是:监听与此套接字对象(clientSocket)建立的链接并接收它,当服务器启动之后,若没有客户端与服务器建立链接,则accept方法会阻塞等待,直到有客户端和服务器建立链接为止。

2️⃣下面这个代码的是读取客户端发来的请求和服务器返回响应,需要使用到Socket当中的getInputStream()和getOutputStream()方法,获取到流对象。

 try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用scanner和printWriter将inputStream和outputStream进行包裹,这样可以将字符流转换为字节流
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
  • getInputStream():返回调用此方法的套接字对象的输入流。
  • getOutputStream():返回调用此方法的套接字对象的输出流。

由于服务器和客户端之间建立链接之后,可能存在多个请求,服务器在读请求的时候怎样确定读到的是一个完整的请求,这里我们做一个简单的约定:每个请求是一个字符串(文本数据),请求和请求之间,使用\n来分割,如果使用字符流并不太好找到\n,但是我们使用scanner将inputStream对象包一下,由于Scanner是按照字符单位处理流对象的,所以我们就可以使用字符流来处理。

Scanner中存在两个方法next()和hasNext(),next()方法读到空白符(空格,换行,制表符,翻页符...)就会结束,这就实现了使用\n分割。

hasNext()方法作用就是判断读取的流对象是否到了结尾,如果到了结尾也就说明对端关闭了。

❗❗注意:这里约定的使用\n来分割读取请求,那么后续客户端发送请求的时候,也得带上\n.由于回显服务器,响应和请求一摸一样,所以响应也要遵守上述的规则

3️⃣将响应写回到客户端,我们也要将字节流转换为字符流,这里我们使用printWriter类包一下,将outputStream对象由字节流转换为字符流,服务器在将响应写回到客户端时,也要对响应进行分割,所以我们可以使用printWriter对象的write方法+分隔符,也可以使用printWriter对象调用println()方法对response对象中的数据写回到客户端。

2.2、客户端代码 

  1. 创建Socket对象,并且指定服务器IP和端口号,这就相当于和服务器建立了链接。
  2. 客户端启动,用户输入请求,把读取的内容构造成请求。
  3. 从服务器读取响应内容
  4. 把响应结果显示在控制台上
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 serverIp,int port) throws IOException {
        //创建客户端socket对象的时候指定服务器IP和端口号
        //这个操作就相当于让客户端和服务器建立tcp链接
        //这里建立上链接之后,服务器中的accept方法的阻塞就会结束,就会得到客户端的socket
        socket = new Socket(serverIp,port);
    }
    public void start(){
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
           
            while(true){
                //1、从键盘上读取用户输入的内容
                System.out.println("->");
                String request = scanner.next();

                //2、把读取的内容构造成请求,发送给服务器
                    //这里发送的请求是带有换行的
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();

                //3、从服务器读取响应内容
                Scanner scannerFromSocket = new Scanner(inputStream);
                String response = scannerFromSocket.next();

                //4、把响应结果显示到控制台上。
                System.out.printf("req: %s;resp: %s\n",request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

❗❗❗注意:在服务器中将请求发送给服务器和客户端中将响应返回到客户端,这两个操作都使用了下面的代码

printWriter.println(request);

这里的操作并不是把数据直接写入到网卡,而是把数据写入到内存的缓冲区中,如果不手动刷新缓冲区,那么就只能等到缓冲区被放满数据之后,才会统一进行刷新,将数据写入到网卡中。所以这就导致一个问题,当我们在客户端发送一个请求之后,服务端并不会立即做出响应。因为请求存在于内存的缓冲区,服务器无法从网卡中读取到请求。

✨要解决这个问题我们可以调用flush()方法,对缓冲区进行手动刷新,这样客户端每写入一个请求,就会刷新一次缓冲区,将请求写入到网卡中,这个时候服务器就可以在网卡中读取到客户端的请求,并作出响应。同理服务器在返回响应的时候,也需要手动刷新缓冲区。

✨了解缓冲区

  • 之前的博客中我们了解过寄存器>内存>硬盘,他们读写速度硬盘是最慢的,但是网卡的读写速度在一般情况下(大多数网卡)是比硬盘慢很多的。为了提高IO效率(读写网卡、读写硬盘都可以视为IO操作),引入了缓冲区,使用缓冲区减少了IO次数,就可以提高整体的效率。
  • 假设写十次,一百次网卡,就先把要写的数据放到一个内存构成的缓冲区中,在同一刷新将缓冲区中的数据写入到网卡中。

理解缓冲区的策略:就像我们嗑瓜子一样,垃圾桶离我们太远,我们将瓜子皮,先拿到手上,手上拿不下的时候,我们将瓜子皮扔到垃圾桶中。这就提高了效率。我们不可能嗑一个瓜子,跑到垃圾桶旁仍一次瓜子皮。(这里的例子只是用来理解缓冲区的工作策略)

上述的回显服务器写完了,但是存在一个很大的问题,作为服务器,就应该可以同时和多个客户端建立链接 。但是我们写的回显服务器只能同时和一个客户端建立链接。

 


 2.3、解决服务器不能同时和多个客户端建立链接的问题 

上述写的服务器不能同时和多个客户端建立链接.这个问题和TCP的特性没有任何关系,只是单纯的属于代码的bug.

 这里我们有两种修改的方法,手动创建多个线程和创建一个线程池。我们通过主线程让服务器和客户端建立链接,手动创建出来的线程或者线程池中的线程处理每个客户端发来的请求。

1️⃣在服务器start方法的while循环中,使用Thread类创建一个线程来处理processConnection方法。通过循环服务器每链接一个客户端,就会创建一个线程。在每个线程中单独处理每个客户端发来的请求。

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
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 clientSocket = serverSocket.accept();
            //如果直接调用processConnection方法,该方法会影响这个循环的二次执行,导致accept(链接)不及时
            //创建新的线程,用新线程来调用processConnection
            //每次来一个新的客户端都会创建一个新的线程
            Thread t = new Thread(()->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            t.start();
        }
    }
    //通过这个方法来处理一个链接
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        //try()这种写法,()中允许写多个流对象,使用;来分割。
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用scanner和printWriter将inputStream和outputStream进行包裹,这样可以将字符流转换为字节流
            Scanner scanner = new Scanner(inputStream);
           
            while(true){
                //1、读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾了(对端关闭了),打印客户端的IP地址和端口号
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request = scanner.next();
                //2、根据请求计算响应
                String response = process(request);
                //3、把响应返回给客户端.不要忘记,响应这里也要带上换行的
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.write(response+"\n");
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %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(9090);
        tcpEchoServer.start();
    }
}

 这里创建的线程什么时候结束,当每个线程的processConnection方法结束,也就是说客户端和服务器断开链接,这个时候当前线程就会结束。

这个修改后的服务器虽然可以同时链接多个客户端,但是我们写的这个服务器很难处理“高并发”,创建的线程太多的话,系统在调用线程上线的时候,就会产生阻塞,有些线程是无法被同时调用上线的,只能是串行执行了。这个问题还是我们电脑的CPU核心数不够,需要解决这个问题,可以增加机器,一个电脑的核心数不够,就使用多个。还可以采用IO多路复用的机制(使用一个线程,可以管理多个socket)。

2️⃣使用线程池代替手动创建线程。

因为手动创建线程池会涉及到频繁的创建和销毁,这就会导致程序的效率比较低。但是使用线程池的话,创建一个客户端链接,当处理完客户端的所有请求的时候,这个线程不会销毁,二hi是还到池子中,下次可以直接使用,这样就省下了销毁线程的时间开销,这就会是程序的效率更高一些。

package network;

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

public class TcpEchoServer {
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        //创建线程池,代替手动创建线程。
            //newCachedThreadPool()创建线程数目动态增长的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("服务器启动!");
        while(true){
            //和客户端建立链接
            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()这种写法,()中允许写多个流对象,使用;来分割。
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用scanner和printWriter将inputStream和outputStream进行包裹,这样可以将字符流转换为字节流
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true){
                //1、读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾了(对端关闭了),打印客户端的IP地址和端口号
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request = scanner.next();
                //2、根据请求计算响应
                String response = process(request);
                //3、把响应返回给客户端.不要忘记,响应这里也要带上换行的
                printWriter.write(response+"\n");
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %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(9090);
        tcpEchoServer.start();
    }
}

 


3、基于TCP socket 写一个简单的单词翻译服务器

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

public class TcpDicServer extends TcpEchoServer{
    private Map<String,String> dict = new HashMap<>();
    public TcpDicServer(int port) throws IOException {
        super(port);
        dict.put("dog","小狗");
        dict.put("cat","小猫");
        dict.put("apple","苹果");
        dict.put("bear","熊");
    }
    @Override
    public String process(String request){
        return dict.getOrDefault(request,"该单词没有查到");
    }

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

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

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

相关文章

【中文编程】青语言

【引言】 青语言主页&#xff1a;https://qingyuyan.cn 青语言文档&#xff1a;https://doc.qingyuyan.cn 青语言社区&#xff1a;https://forum.qingyuyan.cn 青语言仓库&#xff1a;https://gitee.com/NjinN/Qing 长久以来&#xff0c;中文编程一直是开发者社区中争议不断的…

opencv读写png

[1] 测试了怎么手动加 alpha 通道设置透明度后&#xff0c;用 PIL.Image 存 png&#xff0c;通道顺序是 RGBA。这里测试用 opencv 读、写 3、4 通道的 png。 png 可以只存 3 通道的&#xff0c;即不要 alpha&#xff0c;也可以加上 alpha。而无 alpha 时 opencv 的通道顺序是 …

微信视频号加强打击肖像授权侵权短视频

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 视频号安全中心发布公告称&#xff1a;视频号将打击肖像权和侵权的短视频&#xff0c;并在7月份上线“视频授权功能”。 5月份视频号已经下架了3万多条视频&#xff0c;1万多个帐号减少推荐。你看3…

Spring框架-面试题核心概念

目录 1.Spring框架的作用是什么&#xff1f; 2. 什么是DI&#xff1f; 3.什么是AOP&#xff1f; 4.Spring常用注解 5.Spring中的设计模式 6.Spring支持的几种bean的作用域 7.Spring中Bean的生命周期&#xff1f; 8.Spring中的事务管理 9.Spring中的依赖注入方式有几种 10.Sprin…

SpringBoot项目热部署设置

目录 1.设置热部署的好处 2.设置热部署的坏处 3.设置热部署的流程 4.关闭热部署功能 1.设置热部署的好处 Spring Boot 热部署的主要好处是在开发过程中提高了开发效率和体验。它让开发者在修改代码后无需手动重启应用程序&#xff0c;而是可以快速自动重新加载应用程序&…

【新版】系统架构设计师 - 信息安全技术基础知识

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 信息安全技术基础知识考点摘要信息安全基础知识信息安全系统的组成框架信息加密技术对称加密&#xff08;共享密钥&#xff09;非对称加密&#xff08;公开密钥&#xff09;信息摘要数字签名数字信…

el-upload 多文件依次上传(防抖 + 递归)

需求描述 多图上传组件&#xff0c;1-9 张图选择完文件后自动上传&#xff0c;不需要上传按钮来进行手动上传 难点 接口有两种&#xff0c;多图集合上传接口 uploadImgs、单图上传接口 uploadImg 使用 uploadImgs 接口&#xff0c;参数为图片集合 fileList&#xff0c;但是缺…

Spring架构篇--2.7.4 远程通信基础--Netty原理--bind实现客户端acceptread事件处理

前言&#xff1a;本文在Netty 服务端已经实现NioServerSocketChannel 管道的初始化并且绑定了端口后&#xff0c;继续对客户端accept&read事件如何处理进行探究&#xff1b; 1 对客户端accept&read事件的触发&#xff1a; 从之前的ServerBootstrap 的bind 方法中似乎…

JMeter接口测试新思路——灵活使用BeanShell

目录 前言&#xff1a; BeanShell的简介 调用Java方法 执行Class文件 结合实际案例 总结 前言&#xff1a; 在JMeter进行接口测试时&#xff0c;我们可能会遇到需要调用Java方法或者执行Java代码的情况&#xff0c;这时候我们可以使用BeanShell来实现。BeanShell是一个类…

QuintoAndar 如何提高转化率

QuintoAndar 如何提高转化率 ——求关注、求点赞、求分享&#xff0c;二毛拜谢。 QuintoAndar 如何通过提高页面性能来提高每次会话的转化率和页面数 一个专注于优化 Core Web Vitals 并迁移到 Next.js 的项目使转换率提高了 5%&#xff0c;每个会话的页面增加了 87%。 Quint…

07.JavaWeb-Vue+elementUI

1.Vue 功能替代JavaScript和jQuery&#xff0c;基于JavaScript实现的前端框架 1.1配置Vue 1.1.1引入vue库 方法一&#xff1a;通过cdn链接引入最新版本的vue&#xff08;可能会慢些&#xff09; <head><script src"https://cdn.jsdelivr.net/npm/vue">…

基于yolov5开发构建道路路面病害检测识别系统——以捷克、印度、日本三国城市道路实况场景数据为例,开发对比分析模型并分析对应性能

城市道路病害检测是最近比较热门的一个任务领域&#xff0c;核心就是迁移深度学习目前已有的研究成果来实现实时城市道路路面病害的检测识别分析&#xff0c;在我之前的很多博文中都有做过类似桥梁、大坝、基建、隧道等水泥设施裂缝裂痕等目标检测相关的项目&#xff0c;除此之…

利用powershell脚本进行内网渗透

powershell知识点 ps1是powershell脚本的拓展名&#xff0c;就相当于cmd的.bat脚本&#xff0c;但是他更加强大。 获取版本信息 get-host #查看powershell的版本信息$psversiontable #查看powershell的版本信息执行策略 PowerShell 执行策略是一项安全功能&#xff0c;用于控…

softmax之温度系数

1.数学表示 这是传统的softmax&#xff1a; q i e x p ( z i ) ∑ j e x p ( z j ) q_i \frac{exp(z_i)}{\sum_jexp(z_j)} qi​∑j​exp(zj​)exp(zi​)​ 或者写&#xff1a; q i e x p ( z i ) / 1.0 ∑ j e x p ( z j / 1.0 ) q_i \frac{exp(z_i)/1.0}{\sum_jexp(z_j/…

《LCHub低代码指南》:ChatGPT会取代低代码开发平台吗?

目录 一、低代码开发平台的优势 1. 提高开发效率 2. 降低开发成本 3. 提高应用程序的质量 二、ChatGPT的优势 三、ChatGPT是否会取代低代码开发平台 四、结论 随着数字化时代的到来,低代码开发平台已经成为了企业数字化转型的重要工具之一。然而,随着人工智能技术的不…

提升教学质量,监督教室课堂秩序?这招小白也能轻松搞定

在当今快速发展的教育领域&#xff0c;提高教学质量和监督教师的工作表现是学校和教育机构的重要任务之一。 传统的巡课方式存在许多限制&#xff0c;如耗时、人力成本高以及数据收集和分析的困难等。为了应对这些挑战&#xff0c;越来越多的学校和教育机构转向在线巡课系统&am…

微信小程序怎么直播?

我们目前使用的小程序都是支持直播功能的&#xff0c;小程序直播功能是通过小程序直播组件实现的&#xff0c;这是微信为商家提供的实时视频直播工具&#xff0c;可以帮助商家快速通过小程序向用户提供优质的直播内容。同时&#xff0c;借助小程序丰富的营销功能&#xff0c;使…

一、Drools 规则引擎

一、问题引出 现有一个在线申请信用卡的业务场景&#xff0c;用户需要录入个人信息&#xff0c;如下图所示&#xff1a; 通过上图可以看到&#xff0c;用户录入的个人信息包括 姓名、性别、年龄、学历、电话、所在公司、职位、月收入、是否有房、是否有车、是否有信用卡等。录入…

Netty中ServerBootstrap类介绍

一、Netty基本介绍 Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 在保证易于开发的同时还保证了其应用的性能&#xff0c;稳定性和伸缩性。 Netty 是一…

UE5 PCG模块学习1

这次来学习一下UE5.2中正式加入的PCG功能。网上较多的案例是在Landscape地形上创建贴合地面的物体&#xff0c;博主研究了一下&#xff0c;这个案例将创建贴合Mesh的物体&#xff1a; 1.基础生成 1.首先在插件中检查Procedural Content Generation Framework是否已经被开启&…