Solon v3.0.3

三、WebSocket 协议转换为 Socket.D (v2)

</> markdown

此内容 v2.6.0 后支持


WebSocket 支持通过转换为 Socket.D 协议 ,进而支持 Mvc 模式开发(客户端须以 Socket.D 协议交互)。

1、协议转换

引入依赖 Socket.D 协议内核包

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>socketd</artifactId>
</dependency>

尝试把 /mvc/ 频道,升级为 socket.d. 协议。添加协议转换代码

  • 通过 ToSocketdWebSocketListener 监听器,把 WebSocket 转为 Socket.D 协议
  • ToSocketdWebSocketListener 在构造时,需要指定 Socket.D 协议的处理监听器

协议升级后,/mvc/ 频道只能使用 socket.d 客户端连接。(其它频道不受影响)

public class DemoApp {
    public static void main(String[] args) {
        Solon.start(DemoApp.class, args, app->{
            //启用 WebSocket 服务
            app.enableWebSocket(true);
            
            //如果 WebSocket 升级成 Socket.D 协议,不需启用 enableSocketD(true) 
            //enableSocketD 是 org.noear:solon-boot-socketd 插件的启用控制
        });
    }
}

//协议转换处理
@ServerEndpoint("/mvc/")
public class WebSocketAsMvc extends ToSocketdWebSocketListener {
    public WebSocketAsMvc() {
        // clientMode=false,表示服务端模式
        super(new ConfigDefault(false), new EventListener()
                .doOnOpen(s -> {
                    //可以添加点签权?
                    if ("a".equals(s.param("u")) == false) {
                        s.close();
                    }
                })
                .doOn("/demo/hello", (s, m) -> {
                    if (m.isRequest()) {
                        s.reply(m, new StringEntity("{\"code\":200}"));
                    }
                }));
    }
}

2、可以进一步转换为 Mvc 接口

尝试把 /mvc/ 频道,进一步转为 solon 控制器模式开发。(其它频道不受影响)

  • 通过 ToHandlerListener ,再转换为 Solon Handler 接口。从而实现控制器模式开发
@ServerEndpoint("/mvc/")
public class WebSocketAsMvc extends ToSocketdWebSocketListener {
    public WebSocketAsMvc() {
        //将 socket.d 普通监听器,换成 ToHandlerListener 
        //可以对 ToHandlerListener 进行扩展或定制
        super(new ConfigDefault(false), new ToHandlerListener()
                .doOnOpen(s -> {
                    //可以添加点签权?
                    if ("a".equals(s.param("u")) == false) {
                        s.close();
                    }
                }));
        //v2.6.6 后,ToHandlerListener 基类改为 EventListener ,更方便定制
    }
}

//控制器
@Controller
public class HelloController {
    @Socket //不加限定注解的话,可同时支持 http 请求
    @Mapping("/demo/hello")
    public Result hello(long id, String name) { //{code:200,...}
        return Result.succeed();
    }
}

3、使用 Socket.D 进行客户端调用

如果上面是 http-server 自带的 websocket 的服务,即与 http 相同。比如:8080 端口。

  • 以 Java Socket.D 原生接口方式示例

引入依赖包

<!--  提供 sd:ws 传输编解码支持(不同的传输协议,用不同的包) -->
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>socketd-transport-java-websocket</artifactId>
    <version>${socketd.version}</version>
</dependency>

协议升级后,使用 sd:ws 作为协议架构(以示区别,避免混乱)

let clientSession = SocketD.createClient("sd:ws://localhost:8080/mvc/?u=a")
        .open();

//v2.6.6 后,支持实体简化构建。旧版使用 new StringEntity(...) 构建
let request = Entity.of("{id:1,name:'noear'}").metaPut("Content-Type","text/json"),
let response = clientSession.sendAndRequest("/demo/hello",  entity).await();

// event 相当于 http path(注意这个关系)
// data  相当于 http body
// meta  相当于 http header
  • 以 Java Rpc 代理模式示例

再多引入一个依赖包

<!--  提供 SocketdProxy 类 -->
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>nami.channel.socketd</artifactId>
</dependency>

代码示例(以 rpc 代理的方式展示)

//[客户端] 调用 [服务端] 的 mvc
//
HelloService rpc = SocketdProxy.create("sd:ws://localhost:8080/mvc/?u=a", HelloService.class);

System.out.println("MVC result:: " + mvc.hello("noear"));
<script src="/js/socket.d.js"></script>
const clientSession = await SocketD.createClient("sd:ws://127.0.0.1:8080/mvc/?u=a")
        .open();

//添加用户(加个内容类型,方便与 Mvc 对接)
const entity = SocketD.newEntity("{id:1,name:'noear'}").metaPut("Content-Type","text/json"),
clientSession.sendAndRequest("/demo/hello",  entity).thenReply(reply=>{
    alert(reply.dataAsString());
})

// event 相当于 http path(注意这个关系)
// data  相当于 http body
// meta  相当于 http header