精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

WebSocket 的六種集成方式

開發(fā) 前端
今天主要介紹一下前3種方式,畢竟現(xiàn)在的主流框架還是Spring Boot。而后3種其實和Spring Boot并不強行綁定,基于Java就可以支持,不過我也會對后3種做個簡單的介紹,大家先混個眼熟就行了。

由于前段時間我實現(xiàn)了一個庫【Spring Cloud】一個配置注解實現(xiàn) WebSocket 集群方案

以至于我對WebSocket的各種集成方式做了一些研究,目前我所了解到的就是下面這些了(就一個破ws都有這么多花里胡哨的集成方式了?)

  • Javax
  • WebMVC
  • WebFlux
  • Java-WebSocket
  • SocketIO
  • Netty

今天主要介紹一下前3種方式,畢竟現(xiàn)在的主流框架還是Spring Boot

而后3種其實和Spring Boot并不強行綁定,基于Java就可以支持,不過我也會對后3種做個簡單的介紹,大家先混個眼熟就行了

那么接下來我們就來講講前3種方式(Javax,WebMVC,WebFlux)在Spring Boot中的服務端和客戶端配置(客戶端配置也超重要的有木有,平時用不到,用到了卻基本找不到文檔,這也太絕望了)

Javax

在java的擴展包javax.websocket中就定義了一套WebSocket的接口規(guī)范

服務端

一般使用注解的方式來進行配置

第一步

@Component
@ServerEndpoint("/websocket/{type}")
publicclass JavaxWebSocketServerEndpoint {

    @OnOpen
    public void onOpen(Session session, EndpointConfig config,
                       @PathParam(value = "type") String type) {
        //連接建立
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        //連接關閉
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        //接收文本信息
    }

    @OnMessage
    public void onMessage(Session session, PongMessage message) {
        //接收pong信息
    }

    @OnMessage
    public void onMessage(Session session, ByteBuffer message) {
        //接收二進制信息,也可以用byte[]接收
    }

    @OnError
    public void onError(Session session, Throwable e) {
        //異常處理
    }
}

我們在類上添加@ServerEndpoint注解來表示這是一個服務端點,同時可以在注解中配置路徑,這個路徑可以配置成動態(tài)的,使用{}包起來就可以了

  • @OnOpen用來標記對應的方法作為客戶端連接上來之后的回調(diào),Session就相當于和客戶端的連接啦,我們可以把它緩存起來用于發(fā)送消息;通過@PathParam注解就可以獲得動態(tài)路徑中對應值了
  • @OnClose用來標記對應的方法作為客戶端斷開連接之后的回調(diào),我們可以在這個方法中移除對應Session的緩存,同時可以接受一個CloseReason的參數(shù)用于獲取關閉原因
  • @OnMessage用來標記對應的方法作為接收到消息之后的回調(diào),我們可以接受文本消息,二進制消息和pong消息
  • @OnError用來標記對應的方法作為拋出異常之后的回調(diào),可以獲得對應的Session和異常對象

第二步

implementation 'org.springframework.boot:spring-boot-starter-websocket'
@Configuration(proxyBeanMethods = false)
public class JavaxWebSocketConfiguration {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

依賴Spring的WebSocket模塊,手動注入ServerEndpointExporter就可以了

需要注意ServerEndpointExporter是Spring中的類,算是Spring為了支持javax.websocket的原生用法所提供的支持類

冷知識

javax.websocket庫中定義了PongMessage而沒有PingMessage

通過我的測試發(fā)現(xiàn)基本上所有的WebSocket包括前端js自帶的,都實現(xiàn)了自動回復;也就是說當接收到一個ping消息之后,是會自動回應一個pong消息,所以沒有必要再自己接受ping消息來處理了,即我們不會接受到ping消息;關注工眾號:碼猿技術專欄,回復關鍵詞:1111 獲取阿里內(nèi)部Java性能調(diào)優(yōu)手冊!

當然我上面講的ping和pong都是需要使用框架提供的api,如果是我們自己通過Message來自定義心跳數(shù)據(jù)的話是沒有任何的處理的,下面是對應的api

//發(fā)送ping
session.getAsyncRemote().sendPing(ByteBuffer buffer);

//發(fā)送pong
session.getAsyncRemote().sendPong(ByteBuffer buffer);

然后我又發(fā)現(xiàn)js自帶的WebSocket是沒有發(fā)送ping的api的,所以是不是可以猜想當初就是約定服務端發(fā)送ping,客戶端回復pong

客戶端

客戶端也是使用注解配置

第一步

@ClientEndpoint
publicclass JavaxWebSocketClientEndpoint {

    @OnOpen
    public void onOpen(Session session) {
        //連接建立
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        //連接關閉
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        //接收文本消息
    }

    @OnMessage
    public void onMessage(Session session, PongMessage message) {
        //接收pong消息
    }

    @OnMessage
    public void onMessage(Session session, ByteBuffer message) {
        //接收二進制消息
    }

    @OnError
    public void onError(Session session, Throwable e) {
        //異常處理
    }
}

客戶端使用@ClientEndpoint來標記,其他的@OnOpen,@OnClose,@OnMessage,@OnError和服務端一模一樣

第二步

WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(JavaxWebSocketClientEndpoint.class, uri);

我們可以通過ContainerProvider來獲得一個WebSocketContainer,然后調(diào)用connectToServer方法將我們的客戶端類和連接的uri傳入就行了

冷知識

通過ContainerProvider#getWebSocketContainer獲得WebSocketContainer其實是基于SPI實現(xiàn)的

在Spring的環(huán)境中我更推薦大家使用ServletContextAware來獲得,代碼如下

@Component
publicclass JavaxWebSocketContainer implements ServletContextAware {

    privatevolatile WebSocketContainer container;

    public WebSocketContainer getContainer() {
        if (container == null) {
            synchronized (this) {
                if (container == null) {
                    container = ContainerProvider.getWebSocketContainer();
                }
            }
        }
        return container;
    }

    @Override
    public void setServletContext(@NonNull ServletContext servletContext) {
        if (container == null) {
            container = (WebSocketContainer) servletContext
                .getAttribute("javax.websocket.server.ServerContainer");
        }
    }
}

發(fā)消息

Session session = ...

//發(fā)送文本消息
session.getAsyncRemote().sendText(String message);

//發(fā)送二進制消息
session.getAsyncRemote().sendBinary(ByteBuffer message);

//發(fā)送對象消息,會嘗試使用Encoder編碼
session.getAsyncRemote().sendObject(Object message);

//發(fā)送ping
session.getAsyncRemote().sendPing(ByteBuffer buffer);

//發(fā)送pong
session.getAsyncRemote().sendPong(ByteBuffer buffer);

WebMVC

依賴肯定是必不可少的

implementation 'org.springframework.boot:spring-boot-starter-websocket'

服務端

第一步

import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

publicclass ServletWebSocketServerHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(@NonNull WebSocketSession session) throws Exception {
        //連接建立
    }

    @Override
    public void handleMessage(@NonNull WebSocketSession session, @NonNull WebSocketMessage<?> message) throws Exception {
        //接收消息
    }

    @Override
    public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable exception) throws Exception {
        //異常處理
    }

    @Override
    public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus closeStatus) throws Exception {
        //連接關閉
    }

    @Override
    public boolean supportsPartialMessages() {
        //是否支持接收不完整的消息
        returnfalse;
    }
}

我們實現(xiàn)一個WebSocketHandler來處理WebSocket的連接,關閉,消息和異常

第二步

@Configuration
@EnableWebSocket
public class ServletWebSocketServerConfigurer implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
        registry
            //添加處理器到對應的路徑
            .addHandler(new ServletWebSocketServerHandler(), "/websocket")
            .setAllowedOrigins("*");
    }
}

首先需要添加@EnableWebSocket來啟用WebSocket

然后實現(xiàn)WebSocketConfigurer來注冊WebSocket路徑以及對應的WebSocketHandler

握手攔截

提供了HandshakeInterceptor來攔截握手

@Configuration
@EnableWebSocket
publicclass ServletWebSocketServerConfigurer implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
        registry
            //添加處理器到對應的路徑
            .addHandler(new ServletWebSocketServerHandler(), "/websocket")
            //添加握手攔截器
            .addInterceptors(new ServletWebSocketHandshakeInterceptor())
            .setAllowedOrigins("*");
    }
    
    publicstaticclass ServletWebSocketHandshakeInterceptor implements HandshakeInterceptor {

        @Override
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
            //握手之前
            //繼續(xù)握手返回true, 中斷握手返回false
            returnfalse;
        }

        @Override
        public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
            //握手之后
        }
    }
}

冷知識

我在集成的時候發(fā)現(xiàn)這種方式?jīng)]辦法動態(tài)匹配路徑,它的路徑就是固定的,沒辦法使用如/websocket/**這樣的通配符

我在研究了一下之后發(fā)現(xiàn)可以在UrlPathHelper上做點文章

@Configuration
@EnableWebSocket
publicclass ServletWebSocketServerConfigurer implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
        if (registry instanceof ServletWebSocketHandlerRegistry) {
            //替換UrlPathHelper
            ((ServletWebSocketHandlerRegistry) registry)
                .setUrlPathHelper(new PrefixUrlPathHelper("/websocket"));
        }

        registry
            //添加處理器到對應的路徑
            .addHandler(new ServletWebSocketServerHandler(), "/websocket/**")
            .setAllowedOrigins("*");
    }
    
    publicclass PrefixUrlPathHelper extends UrlPathHelper {

        private String prefix;

        @Override
        public@NonNullString resolveAndCacheLookupPath(@NonNull HttpServletRequest request) {
            //獲得原本的Path
            String path = super.resolveAndCacheLookupPath(request);
            //如果是指定前綴就返回對應的通配路徑
            if (path.startsWith(prefix)) {
                return prefix + "/**";
            }
            return path;
        }
    }
}

因為它內(nèi)部實際上就是用一個Map<String, WebSocketHandler>來存的,所以沒有辦法用通配符

主要是有現(xiàn)成的AntPathMatcher實現(xiàn)通配應該不麻煩才對啊

客戶端

第一步

public class ServletWebSocketClientHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(@NonNull WebSocketSession session) throws Exception {
        //連接建立
    }

    @Override
    public void handleMessage(@NonNull WebSocketSession session, @NonNull WebSocketMessage<?> message) throws Exception {
        //接收消息
    }

    @Override
    public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable exception) throws Exception {
        //異常處理
    }

    @Override
    public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus closeStatus) throws Exception {
        //連接關閉
    }

    @Override
    public boolean supportsPartialMessages() {
        //是否支持接收不完整的消息
        returnfalse;
    }
}

和服務端一樣我們需要先實現(xiàn)一個WebSocketHandler來處理WebSocket的連接,關閉,消息和異常

第二步

WebSocketClient client = new StandardWebSocketClient();
WebSocketHandler handler = new ServletWebSocketClientHandler();
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler, uri);
manager.start();

首先我們需要先new一個StandardWebSocketClient,可以傳入一個WebSocketContainer參數(shù),獲得該對象的方式我之前已經(jīng)介紹過了,這邊就先略過

然后new一個WebSocketConnectionManager傳入WebSocketClient,WebSocketHandler還有路徑uri

最后調(diào)用一下WebSocketConnectionManager的start方法就可以啦

冷知識

這里如果大家去看WebSocketClient的實現(xiàn)類就會發(fā)現(xiàn)有StandardWebSocketClient還有JettyWebSocketClient等等,所以大家可以根據(jù)自身項目所使用的容器來選擇不同的WebSocketClient實現(xiàn)類

這里給大家貼一小段Spring適配不同容器WebSocket的代碼

public abstractclass AbstractHandshakeHandler implements HandshakeHandler, Lifecycle {

    privatestaticfinalboolean tomcatWsPresent;

    privatestaticfinalboolean jettyWsPresent;

    privatestaticfinalboolean jetty10WsPresent;

    privatestaticfinalboolean undertowWsPresent;

    privatestaticfinalboolean glassfishWsPresent;

    privatestaticfinalboolean weblogicWsPresent;

    privatestaticfinalboolean websphereWsPresent;

    static {
        ClassLoader classLoader = AbstractHandshakeHandler.class.getClassLoader();
        tomcatWsPresent = ClassUtils.isPresent(
            "org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", classLoader);
        jetty10WsPresent = ClassUtils.isPresent(
            "org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer", classLoader);
        jettyWsPresent = ClassUtils.isPresent(
            "org.eclipse.jetty.websocket.server.WebSocketServerFactory", classLoader);
        undertowWsPresent = ClassUtils.isPresent(
            "io.undertow.websockets.jsr.ServerWebSocketContainer", classLoader);
        glassfishWsPresent = ClassUtils.isPresent(
            "org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", classLoader);
        weblogicWsPresent = ClassUtils.isPresent(
            "weblogic.websocket.tyrus.TyrusServletWriter", classLoader);
        websphereWsPresent = ClassUtils.isPresent(
            "com.ibm.websphere.wsoc.WsWsocServerContainer", classLoader);
    }
}

發(fā)消息

import org.springframework.web.socket.*;

WebSocketSession session = ...

//發(fā)送文本消息
session.sendMessage(new TextMessage(CharSequence message);

//發(fā)送二進制消息
session.sendMessage(new BinaryMessage(ByteBuffer message));

//發(fā)送ping
session.sendMessage(new PingMessage(ByteBuffer message));

//發(fā)送pong
session.sendMessage(new PongMessage(ByteBuffer message));

WebFlux

WebFlux的WebSocket不需要額外的依賴包

服務端

第一步

import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;

publicclass ReactiveWebSocketServerHandler implements WebSocketHandler {

    @NonNull
    @Override
    public Mono<Void> handle(WebSocketSession session) {
        Mono<Void> send = session.send(Flux.create(sink -> {
            //可以持有sink對象在任意時候調(diào)用next發(fā)送消息
            sink.next(WebSocketMessage message);
        })).doOnError(it -> {
            //異常處理
        });

        Mono<Void> receive = session.receive()
                .doOnNext(it -> {
                    //接收消息
                })
                .doOnError(it -> {
                    //異常處理
                })
                .then();

        @SuppressWarnings("all")
        Disposable disposable = session.closeStatus()
                .doOnError(it -> {
                    //異常處理
                })
                .subscribe(it -> {
                    //連接關閉
                });

        return Mono.zip(send, receive).then();
    }
}

首先需要注意這里的WebSocketHandler和WebSocketSession是reactive包下的

  • 通過WebSocketSession#send方法來持有一個FluxSink<WebSocketMessage>來用于發(fā)送消息
  • 通過WebSocketSession#receive來訂閱消息
  • 通過WebSocketSession#closeStatus來訂閱連接關閉事件

第二步

@Component
public class ReactiveWebSocketServerHandlerMapping extends SimpleUrlHandlerMapping {

    public ReactiveWebSocketServerHandlerMapping() {
        Map<String, WebSocketHandler> map = new HashMap<>();
        map.put("/websocket/**", new ReactiveWebSocketServerHandler());
        setUrlMap(map);
        setOrder(100);
    }
}

注冊一個HandlerMapping同時配置路徑和對應的WebSocketHandler

第三步

@Configuration(proxyBeanMethods = false)
public class ReactiveWebSocketConfiguration {

    @Bean
    public WebSocketHandlerAdapter webSocketHandlerAdapter() {
        return new WebSocketHandlerAdapter();
    }
}

注入WebSocketHandlerAdapter

冷知識

我們自定義的HandlerMapping需要設置order,如果不設置,默認為Ordered.LOWEST_PRECEDENCE,會導致這個HandlerMapping被放在最后,當有客戶端連接上來時會被其他的HandlerMapping優(yōu)先匹配上而連接失敗

客戶端

第一步

public class ReactiveWebSocketClientHandler implements WebSocketHandler {

    @NonNull
    @Override
    public Mono<Void> handle(WebSocketSession session) {
        Mono<Void> send = session.send(Flux.create(sink -> {
            //可以持有sink對象在任意時候調(diào)用next發(fā)送消息
            sink.next(WebSocketMessage message);
        })).doOnError(it -> {
            //處理異常
        });

        Mono<Void> receive = session.receive()
                .doOnNext(it -> {
                    //接收消息
                })
                .doOnError(it -> {
                    //異常處理
                })
                .then();

        @SuppressWarnings("all")
        Disposable disposable = session.closeStatus()
                .doOnError(it -> {
                    //異常處理
                })
                .subscribe(it -> {
                    //連接關閉
                });

        return Mono.zip(send, receive).then();
    }
}

客戶端WebSocketHandler的寫法和服務端的一樣

第二步

import org.springframework.web.reactive.socket.client.WebSocketClient;

WebSocketClient client = ReactorNettyWebSocketClient();
WebSocketHandler handler = new ReactiveWebSocketClientHandler();
client.execute(uri, handler).subscribe();

首先我們需要先new一個ReactorNettyWebSocketClient

然后調(diào)用一下WebSocketClient的execute方法傳入路徑uri和WebSocketHandler并繼續(xù)調(diào)用subscribe方法就行啦

冷知識

和WebMVC中的WebSocketClient一樣,Reactive包中的WebSocketClient也有很多實現(xiàn)類,比如ReactorNettyWebSocketClient,JettyWebSocketClient,UndertowWebSocketClient,TomcatWebSocketClient等等,也是需要大家基于自身項目的容器使用不同的實現(xiàn)類

這里也給大家貼一小段Reactive適配不同容器WebSocket的代碼

public class HandshakeWebSocketService implements WebSocketService, Lifecycle {

    privatestaticfinalboolean tomcatPresent;

    privatestaticfinalboolean jettyPresent;

    privatestaticfinalboolean jetty10Present;

    privatestaticfinalboolean undertowPresent;

    privatestaticfinalboolean reactorNettyPresent;

    static {
        ClassLoader loader = HandshakeWebSocketService.class.getClassLoader();
        tomcatPresent = ClassUtils.isPresent("org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", loader);
        jettyPresent = ClassUtils.isPresent("org.eclipse.jetty.websocket.server.WebSocketServerFactory", loader);
        jetty10Present = ClassUtils.isPresent("org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer", loader);
        undertowPresent = ClassUtils.isPresent("io.undertow.websockets.WebSocketProtocolHandshakeHandler", loader);
        reactorNettyPresent = ClassUtils.isPresent("reactor.netty.http.server.HttpServerResponse", loader);
    }
}

發(fā)消息

我們需要使用在WebSocketHandler中獲得的FluxSink<WebSocketMessage>來發(fā)送消息

import org.springframework.web.reactive.socket.CloseStatus;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;

publicclass ReactiveWebSocket {

    privatefinal WebSocketSession session;

    privatefinal FluxSink<WebSocketMessage> sender;

    public ReactiveWebSocket(WebSocketSession session, FluxSink<WebSocketMessage> sender) {
        this.session = session;
        this.sender = sender;
    }

    public String getId() {
        return session.getId();
    }

    public URI getUri() {
        return session.getHandshakeInfo().getUri();
    }

    public void send(Object message) {
        if (message instanceof WebSocketMessage) {
            sender.next((WebSocketMessage) message);
        } elseif (message instanceof String) {
            //發(fā)送文本消息
            sender.next(session.textMessage((String) message));
        } elseif (message instanceof DataBuffer) {
            //發(fā)送二進制消息
            sender.next(session.binaryMessage(factory -> (DataBuffer) message));
        } elseif (message instanceof ByteBuffer) {
            發(fā)送二進制消息
            sender.next(session.binaryMessage(factory -> factory.wrap((ByteBuffer) message)));
        } elseif (message instanceofbyte[]) {
            發(fā)送二進制消息
            sender.next(session.binaryMessage(factory -> factory.wrap((byte[]) message)));
        } else {
            thrownew IllegalArgumentException("Message type not match");
        }
    }

    public void ping() {
        //發(fā)送ping
        sender.next(session.pingMessage(factory -> factory.wrap(ByteBuffer.allocate(0))));
    }

    public void pong() {
        //發(fā)送pong
        sender.next(session.pongMessage(factory -> factory.wrap(ByteBuffer.allocate(0))));
    }

    public void close(CloseStatus reason) {
        sender.complete();
        session.close(reason).subscribe();
    }
}

Java-WebSocket

這是一個純java的第三方庫,專門用于實現(xiàn)WebSocket

Github上已經(jīng)有很詳細的使用教程了,現(xiàn)在有9k+的Star

傳送門:https://github.com/TooTallNate/Java-WebSocket

SocketIO

該庫使用的協(xié)議是經(jīng)過自己封裝的,支持很多的語言,提供了統(tǒng)一的接口,所以需要使用它提供的Server和Client來連接,如socket.io-server-java和socket.io-client-java

這個庫我了解下來主要用于實時聊天等場景,所以如果只是普通的WebSocket功能就有點大材小用了

Github上也有非常詳細的使用文檔,大家如果有興趣可以研究一下

傳送門:https://github.com/socketio

Netty

這個大家應該都比較熟悉了,就算沒用過肯定也聽過

網(wǎng)上的文檔和示例也非常多,我這里就不介紹有的沒的了,Github傳送門

傳送門:https://github.com/netty/netty

責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2017-06-26 10:35:58

前端JavaScript繼承方式

2009-02-11 09:46:00

ASON網(wǎng)絡演進

2019-05-16 13:00:18

異步編程JavaScript回調(diào)函數(shù)

2022-03-23 12:55:50

農(nóng)業(yè)物聯(lián)網(wǎng)

2020-04-27 09:00:00

雙因素認證身份認證生物識別

2022-01-14 10:34:50

黑客隱藏蹤跡網(wǎng)絡安全

2020-07-31 11:12:39

安全威脅網(wǎng)絡攻擊網(wǎng)絡安全

2021-01-08 10:52:22

物聯(lián)網(wǎng)萬物互聯(lián)IoT,Interne

2023-05-10 13:58:13

服務限流系統(tǒng)

2018-08-03 16:40:06

前端前端框架微服務

2025-01-21 10:04:40

Java并發(fā)阻塞隊列

2010-09-13 18:02:46

2022-03-28 20:57:31

私有屬性class屬性和方法

2018-10-19 10:30:51

SD-WAN軟件定義廣域網(wǎng)網(wǎng)絡

2022-07-15 10:30:41

ITCIO

2024-12-18 16:19:51

2023-08-11 13:39:06

首席信息官IT領導

2023-10-30 11:53:37

繼承JS父類

2021-12-15 23:10:34

JS Debugger 前端開發(fā)

2024-04-19 09:26:43

人工智能Llama 3 模型Meta
點贊
收藏

51CTO技術棧公眾號

欧美wwwsss9999| 青春有你2免费观看完整版在线播放高清| 精品中文一区| 欧美视频在线观看一区| 婷婷视频在线播放| 精品人妻一区二区三区蜜桃| 亚洲三级色网| 中日韩美女免费视频网址在线观看| 想看黄色一级片| 嗯~啊~轻一点视频日本在线观看| 91麻豆精东视频| 国产欧美日韩精品丝袜高跟鞋| 国精品无码一区二区三区| 99亚洲乱人伦aⅴ精品| 色综合久久久久综合体| 国产激情在线看| 免费在线黄色影片| 国产一区二区不卡在线 | 婷婷色一区二区三区| 国产精品日本一区二区不卡视频| 精品福利在线看| a级黄色片网站| 欧美新色视频| 成人毛片视频在线观看| 成人性生交大片免费看视频直播 | 国产欧美日本| 久久偷看各类女兵18女厕嘘嘘| 玖玖爱在线精品视频| 久久69成人| 日韩欧美亚洲范冰冰与中字| 国产成人生活片| 日本福利在线| 国产亚洲精品精华液| 国产精品成人一区二区三区| 97人妻人人澡人人爽人人精品| 欧美综合二区| 69**夜色精品国产69乱| 毛片aaaaa| 91精品1区| 色婷婷综合久久久久中文字幕1| 欧美bbbbb性bbbbb视频| 精品国产一区二区三区成人影院 | 偷拍欧美精品| 亚洲一品av免费观看| 中文字幕一区二区三区乱码不卡| 成人亚洲精品| 91精品国产综合久久久久| 一区二区三区免费播放| 亚洲一区资源| 欧美日韩免费网站| 黄色影院一级片| av第一福利在线导航| 亚洲一区二区三区不卡国产欧美| 中文字幕日韩精品一区二区| 在线激情网站| 中文字幕日韩一区| 樱花www成人免费视频| 日本在线免费网| |精品福利一区二区三区| 亚洲乱码一区二区三区三上悠亚| yourporn在线观看中文站| 国产午夜精品一区二区三区嫩草 | 国产免费内射又粗又爽密桃视频| 91在线网址| 国产精品久久久一区麻豆最新章节| 日本中文不卡| 天堂资源在线中文| 综合自拍亚洲综合图不卡区| 影音先锋男人的网站| 羞羞视频在线免费国产| 亚洲国产裸拍裸体视频在线观看乱了| 被灌满精子的波多野结衣| 91桃色在线观看| 狠狠躁夜夜躁人人爽超碰91| 成人亚洲视频在线观看| 日韩三级一区| 精品久久99ma| 国产女主播喷水高潮网红在线| 成人6969www免费视频| 久久精品一偷一偷国产| 久久久久黄色片| 国产精品三上| 国产日韩在线视频| 亚洲精品一区二区三区新线路 | 欧美老年两性高潮| 波多野结衣中文字幕在线播放| 伊人精品综合| 亚洲色图激情小说| 日本精品在线免费观看| 亚洲东热激情| 国产精品扒开腿做爽爽爽的视频| 国产精品久久久久久免费免熟 | 精品亚洲乱码一区二区| 欧美激情91| 91爱视频在线| 国产精品久久久久久久久毛片| 成人av综合一区| 亚洲色图自拍| 操喷在线视频| 欧美喷潮久久久xxxxx| 污污免费在线观看| 久久国产综合| 4438全国成人免费| 91禁在线观看| 久久老女人爱爱| 日本丰满大乳奶| 日本免费久久| 欧美精品一区二区三区蜜臀| 黄色激情小视频| 国产精品美女久久久浪潮软件| 2022国产精品| 午夜视频在线观看网站| 富二代精品短视频| 秋霞午夜鲁丝一区二区| 经典一区二区| 国内揄拍国内精品少妇国语| 91精品国产乱码久久久| 久久网站最新地址| 欧美视频在线免费播放| 精品视频一区二区三区在线观看| 亚洲欧洲黄色网| 日韩av一二三区| 国产在线精品不卡| 亚洲欧美日韩国产yyy| 不卡av影片| 日韩二区三区在线| 久久久久亚洲av无码专区体验| 久久精品国产清高在天天线| 国产在线精品一区| 黄页网站在线观看免费| 91精品国产aⅴ一区二区| 欧美人与禽zoz0善交| 久久狠狠婷婷| 久久精品丝袜高跟鞋| 97人人在线视频| 精品欧美久久久| 久草资源在线视频| 国产福利精品导航| 欧美少妇一区二区三区| 国产精品日本一区二区不卡视频| 日韩性生活视频| 中文字幕你懂的| 国产精品久久久久久久久免费桃花| 成人午夜视频免费在线观看| 久草在线成人| 国产精品96久久久久久| 国产裸舞福利在线视频合集| 色妹子一区二区| 欧美人与性囗牲恔配| 日本熟女一区二区| 亚洲国产一区二区精品专区| 成人资源视频网站免费| 深夜国产在线播放| 精品少妇一区二区三区视频免付费 | 国产亚洲欧美另类中文| 国产精品第5页| 久久嫩草精品久久久精品一| 北条麻妃av高潮尖叫在线观看| 免费毛片在线不卡| 国产精品久久久91| 91伦理视频在线观看| 欧美日本国产一区| 婷婷伊人五月天| 国产成人自拍网| 女人天堂av手机在线| 亚洲警察之高压线| 国产精品一区久久久| 久久99精品久久| 精品精品国产高清a毛片牛牛| 精品国产乱码一区二区| 国产午夜亚洲精品羞羞网站| 奇米视频888| 国产精品www994| 精品久久久久久一区| 日日av拍夜夜添久久免费| 在线观看欧美成人| a级片在线播放| 疯狂做受xxxx欧美肥白少妇| 水蜜桃av无码| 日韩av一区二区在线影视| 在线观看免费黄色片| 免费福利视频一区| 国产精品香蕉av| 在线观看男女av免费网址| 精品无人区太爽高潮在线播放| 久久久国产免费| 一区二区三区在线免费观看| 一本色道综合久久欧美日韩精品| 蜜臀久久99精品久久久画质超高清| 永久免费网站视频在线观看| 国产精品中文字幕制服诱惑| 国产精品嫩草影院一区二区| 牛牛在线精品视频| 在线观看日韩视频| 六月婷婷综合网| 欧美三级电影网| 国产无遮挡又黄又爽| 国产精品久线在线观看| 荫蒂被男人添免费视频| 久久99国产精品免费网站| 妞干网在线观看视频| 日韩精品二区| 久久久久久久久一区| 久久久国产精品入口麻豆| 欧美中文字幕在线观看| 中日韩高清电影网| 中文字幕日韩av综合精品| 欧美特级特黄aaaaaa在线看| 欧美日韩精品欧美日韩精品一| 国产又黄又爽又色| 一区二区成人在线| 国产乱子轮xxx农村| 91色在线porny| 又色又爽又黄18网站| 美女一区二区久久| 超碰网在线观看| 国产精品普通话对白| 无码熟妇人妻av在线电影| 久久综合国产| 日韩精品不卡| 亚洲美女15p| 黄色小网站91| 亚洲综合网狠久久| 91精品视频专区| 国产一区二区三区四区五区3d| 欧美亚洲一区在线| 成入视频在线观看| 午夜精品久久久久久久99热| 2024最新电影免费在线观看| 色偷偷88888欧美精品久久久| 四虎成人免费在线| 日韩av影视在线| 天堂在线视频观看| 亚洲国产成人在线播放| 亚洲成人黄色片| 日韩精品一区二| 亚洲国产综合一区| 日韩视频免费直播| av免费观看在线| 91精品国产麻豆| 国产麻豆91视频| 欧美一区二区三区公司| 国产精品一级视频| 欧美一级生活片| 99热这里只有精品3| 欧美一级片在线观看| www.av导航| 欧美videossexotv100| 高清国产mv在线观看| 精品国产青草久久久久福利| 人妻少妇精品无码专区久久| 亚洲成av人片在线观看香蕉| 深爱五月激情五月| 亚洲美女性视频| 搞黄视频在线观看| 日韩在线观看免费av| 182在线视频| 精品精品国产毛片在线看| 国产一区二区在线网站| 欧亚精品一区| 欧美一区国产一区| 日韩极品一区| 白白操在线视频| 99在线精品视频在线观看| 久久精品.com| 另类调教123区 | abab456成人免费网址| 国产精品大陆在线观看| 日韩福利影视| 91嫩草在线| 清纯唯美亚洲经典中文字幕| 欧美精品中文字幕一区二区| 青青草综合网| 无码人妻精品一区二区蜜桃百度| 亚洲激情视频| 国产精品视频黄色| 国产乱国产乱300精品| 日韩精品视频一区二区| 国产亚洲综合在线| 国产精品久久久久久久精| 亚洲444eee在线观看| 中文字幕手机在线视频| 91精品国产色综合久久| 少妇精品视频一区二区 | 免费一区二区三区| 成人影视亚洲图片在线| 欧美xxxx吸乳| 久久久久久网| 日本成人xxx| 久久久久国产精品厨房| 国产美女福利视频| 精品二区三区线观看| 亚洲在线观看av| 日韩av在线免播放器| 毛片av在线| 奇米成人av国产一区二区三区| 9999精品| 明星裸体视频一区二区| 亚洲一区欧美| 北条麻妃av高潮尖叫在线观看| 国产精品亚洲成人| 神马久久久久久久久久久| 亚洲国产精品尤物yw在线观看| 中文字幕日产av| 日韩av在线网站| 中中文字幕av在线| 国产精品网址在线| 亚洲 欧美 视频| 国产精品a级| 老太脱裤让老头玩ⅹxxxx| 久久国产三级精品| 蜜桃av免费看| 夜夜爽夜夜爽精品视频| 一区二区三区免费观看视频| 日韩国产精品亚洲а∨天堂免| 精产国品自在线www| 国产成人一区二| 秋霞综合在线视频| 日本成人在线不卡| 精品一区二区三区蜜桃| 先锋影音av在线| 色呦呦网站一区| 污污网站在线免费观看| 欧美激情亚洲另类| 久久av网站| 美女黄色片网站| 精久久久久久久久久久| 992在线观看| 欧美色视频在线观看| 欧美精品久久久久久久久久丰满| 久久久噜噜噜久久| 91精品入口| 国产小视频免费| 国产一区二区视频在线| 黑人狂躁日本娇小| 欧美日韩你懂的| 在线观看完整版免费| 国产精品欧美风情| 精品久久久久久久久久久aⅴ| 成年人黄色片视频| 久久久久国产精品免费免费搜索| 国产婷婷色一区二区在线观看| 亚洲精品中文字幕av| 91av亚洲| 日本精品国语自产拍在线观看| 丝瓜av网站精品一区二区| 精品人伦一区二区三电影| 日本韩国欧美在线| 成人免费高清在线播放| 国产精品久久久久久久天堂| 欧美精品系列| 777一区二区| 亚洲精品中文字幕乱码三区| 国产成人精品一区二三区四区五区| 久久综合久久八八| 亚洲综合影院| 人妻少妇精品无码专区二区| 成人av免费网站| 91video| 亚洲欧美一区二区激情| av免费在线一区| 好色先生视频污| 高清在线不卡av| 欧美一级片免费在线观看| 亚洲欧美综合图区| 91精品国产色综合久久不卡粉嫩| 久久久久久久久影视| kk眼镜猥琐国模调教系列一区二区| 999这里只有精品| 一色桃子一区二区| 9999精品免费视频| 东北少妇不带套对白| 久久综合色一综合色88| 亚洲天堂2021av| 欧美俄罗斯性视频| 免费欧美视频| 中文字幕第66页| 午夜影视日本亚洲欧洲精品| www.av在线播放| 成人动漫在线观看视频| 国产精品久久久久久久免费软件| 人妻av无码一区二区三区| 6080午夜不卡| 日韩脚交footjobhd| 一区二区国产日产| 成人黄色国产精品网站大全在线免费观看| 在线观看日本网站| 久久久精品久久| 亚洲瘦老头同性70tv| 992kp免费看片| 欧美日韩亚洲天堂| 成人看片免费| 日韩成人av电影在线| 国产二区国产一区在线观看| 日批视频免费在线观看| 九九综合九九综合| 欧美日韩有码| 极品白嫩丰满美女无套| 在线电影院国产精品| 中文字幕一区久|