首页 > 编程语言 >【java】实现sse调用websocket接口,忽略wss证书并控制sse吐字速度

【java】实现sse调用websocket接口,忽略wss证书并控制sse吐字速度

时间:2024-07-11 14:56:09浏览次数:13  
标签:websocket String wss sseEmitter org sse import public messageUuid

maven

        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.5.3</version>
        </dependency>

AsyncConfig

package com.test.demo.sse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * <p>
 * <code>AsyncConfig</code>
 * </p>
 * Description: 异步配置
 */
@EnableAsync
@Configuration
public class AsyncConfig {

    /**
     * 核心线程数(默认线程数)
     */
    @Value("${sync.corePoolSize:50}")
    private int corePoolSize;
    /**
     * 最大线程数
     */
    @Value("${sync.maxPoolSize:200}")
    private int maxPoolSize;
    /**
     * 缓冲队列数数量
     */
    @Value("${sync.queueCapacity:10000000}")
    private int queueCapacity;

    @Bean
    public Executor executor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数(默认线程数)
        taskExecutor.setCorePoolSize(corePoolSize);
        // 最大线程数
        taskExecutor.setMaxPoolSize(maxPoolSize);
        // 缓冲队列数,默认Integer.MAX_VALUE.
        taskExecutor.setQueueCapacity(queueCapacity);
        // 线程池名前缀
        taskExecutor.setThreadNamePrefix("async-executor-");
        // 允许线程空闲时间(单位:秒),默认:60
        // taskExecutor.setKeepAliveSeconds(60);
        // 线程池对拒绝任务的处理策略,默认值AbortPolicy
        // taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
        taskExecutor.initialize();

        return taskExecutor;
    }

    @Bean
    public ScheduledExecutorService scheduledExecutorService() {
        return Executors.newScheduledThreadPool(corePoolSize,
                new CustomizableThreadFactory("schedule-executor-"));
    }
}

SpringContextUtils

package com.test.demo.sse;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * Spring ApplicationContext 工具类
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {

    /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getDomain() {
        HttpServletRequest request = getHttpServletRequest();
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    public static String getOrigin() {
        HttpServletRequest request = getHttpServletRequest();
        return request.getHeader("Origin");
    }

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    /**
     * 发布事件
     *
     * @param event
     */
    public static void publishEvent(ApplicationEvent event) {
        if (applicationContext == null) {
            return;
        }
        applicationContext.publishEvent(event);
    }
}

MySseEmitter

package com.test.demo.sse;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * <p>
 * <code>MySseEmitter</code>
 * </p>
 * Description:  解决SseEmitter浏览器下中文乱码问题
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class MySseEmitter extends SseEmitter {
    /**
     * websocket返回的所有信息,只用于将消息发送到前端
     */
    private StringBuilder totalAnswer = new StringBuilder();
    /**
     * websocket返回的所有信息,用于最终存储的消息内容
     */
    private StringBuilder totalAnswerStorage = new StringBuilder();
    /**
     * 链接是否已主动断开,,true:已主动断开,false:未断开
     */
    private boolean disconnected = false;
    /**
     * 本条消息的唯一id
     */
    private String messageUuid = UUID.randomUUID().toString();
    /**
     * 本次会话的唯一id
     */
    private String conversationUuid = UUID.randomUUID().toString();
    /**
     * 是否匀速返回,true:需要匀速,false:不需要匀速
     */
    private boolean speedControl;
    /**
     * 是否已经开始匀速返回信息,true:已经开始,false:还没有开始
     */
    private boolean startSendMsgWithSpeedControl = false;
    /**
     * 已经发送的消息的长度
     */
    private int sendLength = 0;
    /**
     * 所有消息是否已经全部匀速返回,true:已经全部返回,false:还没有全部返回
     */
    private boolean endSendMsgWithSpeedControl = false;
    /**
     * 匀速吐字间隔时间,单位:毫秒
     */
    private long sleepTime = 20L;
    /**
     * 匀速发送消息时每次返回多少个字符
     */
    private int sendMsgSpeed = 1;
    /**
     * 超时时间,单位:毫秒
     */
    private long timeout;

    /**
     * 当前登录人
     */
    private String userUid;
    /**
     * 当前登录人的问题
     */
    private String userQuestion;


    /**
     * 解决中文乱码
     *
     * @param outputMessage
     */
    @Override
    protected void extendResponse(ServerHttpResponse outputMessage) {
        super.extendResponse(outputMessage);
        HttpHeaders headers = outputMessage.getHeaders();
        headers.setContentType(new MediaType(MediaType.TEXT_EVENT_STREAM, StandardCharsets.UTF_8));
    }

    /**
     * 创建SSE对象
     *
     * @param speedControl 是否开启匀速,true:开启,false:关闭
     * @param timeout      超时时间,单位:毫秒
     * @param userUid      当前登录人
     * @param userQuestion 当前登录人的问题
     */
    public MySseEmitter(boolean speedControl, long timeout, String userUid, String userQuestion) {
        // 设置超时时间,单位:毫秒
        super(timeout);
        this.speedControl = speedControl;
        this.timeout = timeout;
        this.userUid = userUid;
        this.userQuestion = userQuestion;
    }

    /**
     * 自定义发送消息方法
     *
     * @param message    具体消息
     * @param msgStorage 本次发送的消息内容是否需要进行存储,true:需要,false:不需要
     * @return 是否需要关闭链接,true:是,false:否
     */
    public boolean mySend(String message, boolean msgStorage) {
        try {
            if (StringUtils.isNotEmpty(message)) {
                // 处理换行,PC换行\r、\n、、\r\n都行,移动只能\r\n
                message = message.replaceAll("\r", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n", "\r\n");

                this.totalAnswer.append(message);
                if (msgStorage) {
                    this.totalAnswerStorage.append(message);
                }
                if (!this.speedControl) {
                    super.send(message);
                } else if (!this.startSendMsgWithSpeedControl && !this.disconnected) {
                    // 异步发送
                    SpringContextUtils.getBean(AsyncService.class).sendMsgWithSpeedControl(this);
                }
            }
        } catch (Exception e) {
            log.error("==>MySseEmitter send error,conversationUuid:{}", this.conversationUuid, e);
            this.disconnected = true;
        }
        return this.disconnected;
    }

    /**
     * 断开连接
     *
     * @param msgStorageType 消息处理类型,0:不存储,1:本地数据库存储
     */
    public void myComplete(String msgStorageType) {
        try {
            if (!this.speedControl || this.disconnected || this.endSendMsgWithSpeedControl) {
                super.complete();
            } else {
                Future<?> future = SpringContextUtils.getBean(ScheduledExecutorService.class).scheduleAtFixedRate(() -> {
                    if (this.endSendMsgWithSpeedControl) {
                        log.info("==>当前消息已全部返回完成,主动断开与端上链接,conversationUuid:{}", conversationUuid);
                        throw new RuntimeException("==>当前消息已全部返回完成,主动断开与端上链接,conversationUuid:" + conversationUuid);
                    }
                }, this.sleepTime, this.sleepTime, TimeUnit.MILLISECONDS);
                try {
                    // 超时时间,单位:毫秒
                    future.get(timeout, TimeUnit.MILLISECONDS);
                } catch (TimeoutException | ExecutionException e) {
                    log.info("==>等待断开链接任务执行结束,conversationUuid:{}", conversationUuid);
                    // 取消任务
                    future.cancel(true);
                } catch (Exception e) {
                    log.error("==>等待断开链接任务执行异常,conversationUuid:{}", conversationUuid, e);
                    // 取消任务
                    future.cancel(true);
                }

                super.complete();
            }
        } catch (Exception ignore) {
        }

        if ("1".equals(msgStorageType)) {
            // 本地数据库存储消息,异步保存数据
            SpringContextUtils.getBean(AsyncService.class).saveMsg(this.messageUuid, this.conversationUuid,
                    this.userUid, this.userQuestion, this.totalAnswerStorage.toString());
        }
    }
}

MyWebSocketClient

package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;

import javax.net.ssl.*;
import java.net.Socket;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * <p>
 * <code>MyWebSocketClient</code>
 * </p>
 * Description: 自定义WebSocketClient,忽略wss证书
 */
@Slf4j
public abstract class MyWebSocketClient extends WebSocketClient {

    /**
     * 创建WebSocketClient
     *
     * @param serverUri      websocket 地址
     * @param connectTimeout 连接超时时间,单位:毫秒
     */
    public MyWebSocketClient(URI serverUri, int connectTimeout) {
        // 设置连接超时时间
        super(serverUri, new Draft_6455(), null, connectTimeout);
        // 设置不验证SSL证书的SSLContext
        TrustManager[] trustAllCerts = new TrustManager[]{new X509ExtendedTrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

            }

            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }
        }};

        try {
            SSLContext ssl = SSLContext.getInstance("SSL");
            ssl.init(null, trustAllCerts, new java.security.SecureRandom());
            SSLSocketFactory socketFactory = ssl.getSocketFactory();
            this.setSocketFactory(socketFactory);
        } catch (Exception e) {
            log.error("==>初始化SSLContext失败", e);
        }
    }
}

MyWebSocketClientHelper

package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

/**
 * <p>
 * <code>MyWebSocketClientHelper</code>
 * </p>
 * Description:
 */
@Slf4j
public class MyWebSocketClientHelper {

    /**
     * WebSocketClient连接并发送消息
     *
     * @param sseEmitter     sse链接
     * @param msgStorageType 消息处理类型,0:不存储,1:本地数据库存储
     */
    public static void connectAndSend(MySseEmitter sseEmitter, String msgStorageType) {
        String commonErrorMsg = "通用错误信息,报错啦";
        String messageUuid = sseEmitter.getMessageUuid();

        WebSocketClient client = null;
        try {
            client = new MyWebSocketClient(new URI("wss://xxxxx"), Integer.parseInt(Long.toString(sseEmitter.getTimeout()))) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    log.info("==>connect success,messageUuid:{}", messageUuid);
                    try {
                        String requestParam = "xxxxxxx";
                        this.send(requestParam);
                    } catch (Exception e) {
                        log.error("==>sendRequest error,messageUuid:{}", messageUuid, e);
                        throw e;
                    }
                }

                @Override
                public void onMessage(String result) {
                    log.info("==>messageUuid:{},onMessage:{}", messageUuid, result);
                    try {
                        sseEmitter.mySend(result, true);
                    } catch (Exception e) {
                        log.error("==>onMessage error,messageUuid:{}", messageUuid, e);
                    }
                }

                @Override
                public void onClose(int code, String reason, boolean remote) {
                    // 1. code(int类型):表示关闭连接的原因,通常是一个整数。例如,如果连接正常关闭,code的值可能是1000(表示正常关闭);如果连接因为服务器主动关闭而关闭,code的值可能是1006(表示服务器端强制关闭)。
                    // 2. reason(String类型):表示关闭连接的原因,通常是一段文本描述。这个参数是可选的,如果没有提供原因,可以传递一个空字符串或者null。
                    // 3. remote(boolean类型):表示连接是否被清理。如果为true,表示连接正常关闭;如果为false,表示连接异常关闭。
                    log.info("==>onClose,messageUuid:{},code:{},reason:{},remote:{}", messageUuid, code, reason, remote);
                    try {
                        if (!sseEmitter.isDisconnected() && StringUtils.isBlank(sseEmitter.getTotalAnswer().toString())) {
                            sseEmitter.mySend(commonErrorMsg, true);
                        }
                        sseEmitter.myComplete(msgStorageType);
                    } catch (Exception ignored) {
                    }
                }

                @Override
                public void one rror(Exception e) {
                    log.error("==>onError,messageUuid:{}", messageUuid, e);
                    try {
                        this.close();
                    } catch (Exception ignored) {
                    }
                }
            };
            client.connect();
        } catch (Exception e) {
            log.error("==>WebSocketClientConnectAndSend error,messageUuid:{}", messageUuid, e);
            try {
                if (client != null) {
                    client.close();
                }
            } catch (Exception ignored) {
            }
        }
    }
}

AsyncService

package com.test.demo.sse;

/**
 * <p>
 * <code>AsyncService</code>
 * </p>
 * Description:
 */
public interface AsyncService {

    /**
     * 异步匀速返回消息
     *
     * @param sseEmitter
     */
    void sendMsgWithSpeedControl(MySseEmitter sseEmitter);

    /**
     * 异步保存消息
     *
     * @param messageUuid        消息uid
     * @param conversationUuid   会话uid
     * @param userUid            当前登录人uid
     * @param userQuestion       用户输入的问题
     * @param totalAnswerStorage websocket返回的具体消息内容
     */
    void saveMsg(String messageUuid, String conversationUuid, String userUid, String userQuestion, String totalAnswerStorage);
}

AsyncServiceImpl

package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.*;

/**
 * <p>
 * <code>AsyncServiceImpl</code>
 * </p>
 * Description:
 */
@Slf4j
@Async
@Service
public class AsyncServiceImpl implements AsyncService {

    @Lazy
    @Autowired
    ScheduledExecutorService scheduledExecutorService;

    @Override
    public void sendMsgWithSpeedControl(MySseEmitter sseEmitter) {
        sseEmitter.setStartSendMsgWithSpeedControl(true);
        sseEmitter.setEndSendMsgWithSpeedControl(false);

        final String messageUuid = sseEmitter.getMessageUuid();

        // 使用scheduleAtFixedRate方法安排任务
        Future<?> future = scheduledExecutorService.scheduleAtFixedRate(() -> {
            int sendLength = sseEmitter.getSendLength();
            int sendMsgSpeed = sseEmitter.getSendMsgSpeed();
            // 当前时刻,所有的消息
            String nowAllAnswer = sseEmitter.getTotalAnswer().toString();
            int totalAnswerLength = nowAllAnswer.length();
            if (sendLength >= totalAnswerLength) {
                log.info("==>当前时刻所有消息已全部发送完成,任务执行结束,messageUuid:{}", messageUuid);
                throw new RuntimeException("==>当前时刻所有消息已全部发送完成,任务执行结束,messageUuid:" + messageUuid);
            }

            String message;
            if ((sendLength + sendMsgSpeed) > totalAnswerLength) {
                message = nowAllAnswer.substring(sendLength);
            } else {
                message = nowAllAnswer.substring(sendLength, sendLength + sendMsgSpeed);
            }

            if (message.endsWith("\r") && (sendLength + sendMsgSpeed + 1) <= totalAnswerLength) {
                message = nowAllAnswer.substring(sendLength, sendLength + sendMsgSpeed + 1);
            }

            try {
                sseEmitter.send(message);
            } catch (Exception e) {
                sseEmitter.setDisconnected(true);
                log.info("==>发送消息失败,视为端上主动断开链接,任务执行结束,messageUuid:{}", messageUuid);
                throw new RuntimeException("==>发送消息失败,视为端上主动断开链接,任务执行结束,messageUuid:" + messageUuid);
            }
            sendLength += message.length();
            sseEmitter.setSendLength(sendLength);
        }, sseEmitter.getSleepTime(), sseEmitter.getSleepTime(), TimeUnit.MILLISECONDS);

        // 尝试获取任务结果,如果超过超时时间则抛出TimeoutException异常
        try {
            // 超时时间,单位:毫秒
            future.get(sseEmitter.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException | ExecutionException e) {
            log.info("==>任务执行结束,messageUuid:{}", messageUuid);
            // 取消任务
            future.cancel(true);
        } catch (Exception e) {
            log.error("==>任务执行异常,messageUuid:{}", messageUuid, e);
            // 取消任务
            future.cancel(true);
        }

        sseEmitter.setStartSendMsgWithSpeedControl(false);
        sseEmitter.setEndSendMsgWithSpeedControl(true);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMsg(String messageUuid, String conversationUuid, String userUid, String userQuestion, String totalAnswerStorage) {
        log.info("==>保存会话和信息,messageUuid:{},conversationUuid:{},userUid:{},userQuestion:{},totalAnswerStorage:{}",
                messageUuid, conversationUuid, userUid, userQuestion, totalAnswerStorage);
        // 会话不存在的,新建会话并保存


        // 保存消息
    }
}

使用方法

package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * <p>
 * <code>SseTestController</code>
 * </p>
 * Description:
 */
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseTestController {

    /**
     * 用户提问提问
     *
     * @param input 输入信息
     * @return
     */
    @PostMapping(value = "/ask")
    public SseEmitter ask(@RequestBody @Valid Input input) {
        MySseEmitter sseEmitter = new MySseEmitter(true, 60000L, input.getUserUid(), input.getUserQuestion);
        try {
            MyWebSocketClientHelper.connectAndSend(sseEmitter, "1");
        } catch (Exception e) {
            log.error("ask error", e);
            sseEmitter.myComplete("1");
        }
        return sseEmitter;
    }
}

标签:websocket,String,wss,sseEmitter,org,sse,import,public,messageUuid
From: https://blog.csdn.net/muguazhi/article/details/140345010

相关文章

  • 解决PyTorch中的RuntimeError: CUDA error: device-side assert triggered
    解决PyTorch中的RuntimeError:CUDAerror:device-sideasserttriggered......
  • Solidity:assembly
    在Solidity中,assembly是一个内嵌的低级语言,它允许开发者直接编写EVM(以太坊虚拟机)字节码。这种能力使得开发者可以更精细地控制智能合约的行为,并且在某些情况下可以提高性能和减少gas费用。然而,使用assembly也增加了代码的复杂性和出错的可能性,因此应谨慎使用。为什么使用Assembly......
  • QPushButton的checked和pressed样式设置无效是因为优先级问题
    设置QPushButton想要设置pressed状态的图标,但是尝试了很多次都没有效果,原来是按照优先级来的,位置越往下优先级越高,hover状态时在最下面,所以鼠标在按钮上时,hover优先级最高,所以无论pressed还是checked都无法显示正确的图标,所以要调整下顺序; QPushButton{border-image:url......
  • go 使用websocket
    packagechatimport( "encoding/json" "github.com/gorilla/websocket" "github.com/zeromicro/go-zero/core/logx" "log" "net/http" "sync")typeClientstruct{ conn*websocket.Conn......
  • Java工程中读取resources目录下properties文件的方式,从上图可知,当工程部署在服务器下
    Java工程中读取resources目录下properties文件的方式,从上图可知,当工程部署在服务器下时,配置文件以及代码都是在对应的classes文件夹下二、具体读取方法1、当需要读取当前路径下的properties文件时,即在本地没有部署到具体服务器上的情况:Filefile=newFile(“src/main/re......
  • C/C++ 断言 assert 的使用方法和注意事项
    C/C++中的断言(Assertion)是一种调试辅助工具,主要用于在开发过程中检测程序中的错误。断言对于确保程序的内部状态满足特定条件非常有用。如果条件为真(即,预期的条件得到了满足),程序可以继续执行。如果条件为假,则断言失败,程序会报告错误并终止执行。使用方法在C语言中,断言是通过a......
  • CF292C Beautiful IP Addresses 题解(两种写法)
    题意一个IP地址是一个32位的2进制整数,分成四组8位的2进制整数(没有前导0)。比如说,0.255.1.123 是一个正确的IP地址,而0.256.1.123 和 0.255.1.01 不是正确的。定义一个合法的回文IP地址为BeautifulIPAddress(回文地址就是去掉“.”后是个回文字符串的地......
  • SpringBoot引入WebSocket
    WebSocket是一种在客户端和服务器之间提供低延迟、全双工通信的网络协议。它允许双方在建立一次连接后,进行实时、持续的数据交换,无需像HTTP那样为每一个请求和响应建立新的连接。WebSocket的设计初衷是解决传统HTTP协议在实时通信方面的不足,比如实现实时聊天、游戏、股票报价等......
  • 使用WebSocket和C语言实现一个简单的计算器
    在现代Web开发中,WebSocket已经成为实时通信的重要工具。本文将介绍如何使用WebSocket与C语言结合,实现一个简单的计算器应用。我们将通过Go语言作为中间层,调用C语言编写的计算函数,并通过WebSocket与前端进行交互。在使用本文章代码开发过程中遇到问题,可参考博主的另外两篇博客......
  • C#使用Blazor编译WebAssembly供前端调用(一),关于SkiaSharp相关问题
    目前信创热潮开始掀起,而C#很多行业开发的都是桌面端,迁移到网页端常常会因为很多库不支持或者不友好导致项目一直卡着。最近一直在网上找灵感,偶然发现WebAssembly,一开始我还没不知道这是什么,后面发现目前主流浏览器都支持这一技术。我们看一下这个WebAssembly简介如下而后我......