此内容 v2.6.0 后支持

---

WebSocket 支持通过转换为 [Socket.D 协议](https://gitee.com/noear/socketd) ，进而支持 Mvc 模式开发（客户端须以 Socket.D 协议交互）。

### 1、协议转换


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

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


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

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

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

```java
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-server-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 接口。从而实现控制器模式开发

```java
@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 原生接口方式示例

引入依赖包

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

协议升级后，使用 `sd:ws` 作为协议架构（以示区别，避免混乱）

```java
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 代理模式示例

再多引入一个依赖包

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

代码示例（以 rpc 代理的方式展示）

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

System.out.println("MVC result:: " + mvc.hello("noear"));
```

* 以 Javascript 客户端示例（具体参考：[Socket.D - JavaScript 开发](https://socketd.noear.org/article/694)）


```html
<script src="/js/socket.d.js"></script>
```

```javascript
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
```
