重构服务端为接口式(WebSocketServer(port)+addHandler 工厂,每连接独立 handler),组管理与发送收敛为链式门面 group(name)/conn(connId),统一 send 三重重载,升级版本 1.1.0
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* simcu::websocket.server —— WebSocket 服务端 handler 接口。
|
||||
*
|
||||
* handler 只关心连接生命周期回调:onConnect / onMessage / onClose / onError。
|
||||
* 端点路径由 WebSocketServer.addHandler(path, factory) 注册时指定,handler 自身不感知。
|
||||
* 每个连接在握手成功时由工厂创建一个独立的 handler 实例,因此 handler 内可持有该
|
||||
* 连接的会话状态(如 connectionId / 组信息),但不要持有会跨连接共享的 scoped 服务。
|
||||
*/
|
||||
package simcu::websocket.server
|
||||
|
||||
import simcu::websocket.common.WebSocketMessage
|
||||
|
||||
/// WebSocket 服务端 handler 接口(多 handler 按注册路径路由)。
|
||||
public interface IWebsocketHandler {
|
||||
/// 握手成功、连接建立后触发。
|
||||
func onConnect(conn: WebSocketConnection): Unit
|
||||
|
||||
/// 连接收到消息时触发。
|
||||
func onMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit
|
||||
|
||||
/// 连接关闭时触发(参数为关闭码、关闭原因)。
|
||||
func onClose(conn: WebSocketConnection, code: Int64, reason: String): Unit
|
||||
|
||||
/// 连接运行时错误时触发。
|
||||
func onError(conn: WebSocketConnection, e: Exception): Unit
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
/*
|
||||
* simcu::websocket.server —— 服务端单条 WebSocket 连接。
|
||||
*
|
||||
* 由 WebSocketServer 在握手成功后创建并暴露给 on('connection') 回调。
|
||||
* 提供 send/sendText/sendBinary/ping/close/terminate,以及连接级事件:
|
||||
* conn.on("message", { (msg) => ... })
|
||||
* conn.on("close", { (code, reason) => ... })
|
||||
* conn.on("error", { (e) => ... })
|
||||
* 由 WebSocketServer 在握手成功后创建并交给匹配的 IWebsocketHandler。
|
||||
* 提供 send(三重重载:WebSocketMessage / String / Array<Byte>)/ ping / close / terminate,以及只读属性
|
||||
* connectionId / readyState / remoteAddress / requestPath / requestQuery。
|
||||
*/
|
||||
package simcu::websocket.server
|
||||
|
||||
@@ -19,11 +17,11 @@ import simcu::websocket.common.MessageType
|
||||
import simcu::websocket.common.ReadyState
|
||||
import simcu::websocket.common.WebSocketMessage
|
||||
import simcu::websocket.common.WebSocketException
|
||||
import simcu::websocket.common.WsEvents
|
||||
|
||||
/// 服务端单条 WebSocket 连接(事件回调式)。
|
||||
/// 服务端单条 WebSocket 连接(由对应 handler 处理回调)。
|
||||
public class WebSocketConnection {
|
||||
private let parent: ?WebSocketServer
|
||||
private let handler: IWebsocketHandler
|
||||
private let frame: FrameStream
|
||||
private let stateLock = Mutex()
|
||||
private var state: ReadyState = ReadyState.Open
|
||||
@@ -31,17 +29,54 @@ public class WebSocketConnection {
|
||||
/// 连接唯一编号(由服务端分配,用于连接集合的引用比较)。
|
||||
internal let connId: Int64
|
||||
|
||||
/// 连接级事件:收到消息(不含父级 connection 参数)。
|
||||
public var onMessage: (WebSocketMessage) -> Unit = { m => () }
|
||||
/// 连接级事件:连接关闭,参数 (关闭码, 关闭原因)。
|
||||
public var onClose: (Int64, String) -> Unit = { c, r => () }
|
||||
/// 连接级事件:运行时错误。
|
||||
public var onError: (Exception) -> Unit = { e => () }
|
||||
/// 连接唯一编号(对外只读,业务层用于关联连接状态)。
|
||||
public prop connectionId: Int64 {
|
||||
get() { connId }
|
||||
}
|
||||
|
||||
internal init(parent: ?WebSocketServer, frame: FrameStream, connId: Int64) {
|
||||
/// 握手请求路径(不含 query,如 "/ws/server")。
|
||||
public let requestPath: String
|
||||
/// 握手请求 query 字符串(原始形式,不含 '?';无 query 时为空字符串,如 "serverId=srv-1")。
|
||||
public let requestQuery: String
|
||||
|
||||
/// 回调归属的 handler(由 WebSocketServer 按路径匹配后注入)。
|
||||
internal prop wsHandler: IWebsocketHandler {
|
||||
get() { handler }
|
||||
}
|
||||
|
||||
/// 归属的服务端(用于组管理 / 单播 / 组播委托,None 表示已脱离服务端)。
|
||||
public prop server: ?WebSocketServer {
|
||||
get() { parent }
|
||||
}
|
||||
|
||||
internal init(parent: ?WebSocketServer, handler: IWebsocketHandler, frame: FrameStream,
|
||||
connId: Int64, requestPath: String, requestQuery: String) {
|
||||
this.parent = parent
|
||||
this.handler = handler
|
||||
this.frame = frame
|
||||
this.connId = connId
|
||||
this.requestPath = requestPath
|
||||
this.requestQuery = requestQuery
|
||||
}
|
||||
|
||||
/// 取 query 参数值(如 "?serverId=srv-1" → queryParam("serverId") = Some("srv-1"))。
|
||||
/// 参数不存在返回 None;参数存在但无值时返回 Some("")。
|
||||
public func queryParam(name: String): ?String {
|
||||
if (requestQuery.isEmpty()) {
|
||||
return None
|
||||
}
|
||||
for (pair in requestQuery.split("&")) {
|
||||
if (let Some(e) <- pair.indexOf("=")) {
|
||||
if (pair[0..e] == name) {
|
||||
return Some(pair[e + 1..])
|
||||
}
|
||||
} else {
|
||||
if (pair == name) {
|
||||
return Some("")
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// 当前连接状态。
|
||||
@@ -66,34 +101,7 @@ public class WebSocketConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册连接级事件。支持 "message" / "close" / "error"。
|
||||
public func on(event: String, listener: (WebSocketMessage) -> Unit): Unit {
|
||||
if (event == WsEvents.message) {
|
||||
onMessage = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册连接级事件。支持 "close" / "error"。
|
||||
public func on(event: String, listener: (Int64, String) -> Unit): Unit {
|
||||
if (event == WsEvents.close) {
|
||||
onClose = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册连接级事件。支持 "error"。
|
||||
public func on(event: String, listener: (Exception) -> Unit): Unit {
|
||||
if (event == WsEvents.error) {
|
||||
onError = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送消息。
|
||||
/// 发送消息(三重重载:WebSocketMessage / String 文本 / Array<Byte> 二进制)。
|
||||
public func send(message: WebSocketMessage): Unit {
|
||||
ensureOpen()
|
||||
if (message.`type` == MessageType.Text) {
|
||||
@@ -104,17 +112,33 @@ public class WebSocketConnection {
|
||||
}
|
||||
|
||||
/// 发送文本消息。
|
||||
public func sendText(text: String): Unit {
|
||||
public func send(text: String): Unit {
|
||||
ensureOpen()
|
||||
frame.writeText(text)
|
||||
}
|
||||
|
||||
/// 发送二进制消息。
|
||||
public func sendBinary(data: Array<Byte>): Unit {
|
||||
public func send(data: Array<Byte>): Unit {
|
||||
ensureOpen()
|
||||
frame.writeBinary(data)
|
||||
}
|
||||
|
||||
// ===== 组管理(经 server 转发) =====
|
||||
|
||||
/// 将当前连接加入组(组不存在则自动创建)。
|
||||
public func joinGroup(group: String): Unit {
|
||||
if (let Some(s) <- parent) {
|
||||
s.group(group).join(this)
|
||||
}
|
||||
}
|
||||
|
||||
/// 将当前连接移出组。
|
||||
public func leaveGroup(group: String): Unit {
|
||||
if (let Some(s) <- parent) {
|
||||
s.group(group).leave(this)
|
||||
}
|
||||
}
|
||||
|
||||
/// 发送 Ping(心跳保活)。
|
||||
public func ping(): Unit {
|
||||
ensureOpen()
|
||||
@@ -193,20 +217,14 @@ public class WebSocketConnection {
|
||||
} else {
|
||||
WebSocketMessage(MessageType.Binary, f.payload)
|
||||
}
|
||||
onMessage(msg)
|
||||
if (let Some(p) <- parent) {
|
||||
p.fireMessage(this, msg)
|
||||
}
|
||||
handler.onMessage(this, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
code = 1006
|
||||
if (state != ReadyState.Closing) {
|
||||
onError(e)
|
||||
if (let Some(p) <- parent) {
|
||||
p.fireError(e)
|
||||
}
|
||||
handler.onError(this, e)
|
||||
}
|
||||
}
|
||||
synchronized(stateLock) {
|
||||
@@ -220,9 +238,6 @@ public class WebSocketConnection {
|
||||
if (let Some(p) <- parent) {
|
||||
p.removeConnection(this)
|
||||
}
|
||||
onClose(code, reason)
|
||||
if (let Some(p) <- parent) {
|
||||
p.fireClose(this, code, reason)
|
||||
}
|
||||
handler.onClose(this, code, reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* simcu::websocket.server —— 链式门面(组管理 + 发送)。
|
||||
*
|
||||
* 组管理与发送的实现都收敛在这里,由 WebSocketServer.group(name)/conn(connId) 返回:
|
||||
* ws.group("authed").join(conn).send("hello").send(data) // 入组 + 组播(send 三重重载)
|
||||
* ws.conn(connId).send("hi").send(data) // 单播
|
||||
* ws.group("authed").count() / isEmpty() / connIds() // 组信息
|
||||
* ws.conn(connId).groups() // 连接所属组
|
||||
*
|
||||
* 组存储(server.connGroups)由 server 持有并负责连接断开自动清组;
|
||||
* 门面在 server.connLock 保护下直接操作。连接不存在/发送失败静默忽略,单个失败不影响其余。
|
||||
*/
|
||||
|
||||
package simcu::websocket.server
|
||||
|
||||
import std.collection.*
|
||||
import simcu::websocket.common.WebSocketMessage
|
||||
|
||||
/// 判断连接Id是否在列表中(组管理辅助)。
|
||||
internal func listHasConnId(list: ArrayList<Int64>, id: Int64): Bool {
|
||||
for (x in list) {
|
||||
if (x == id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 从列表中移除所有匹配的连接Id(组管理辅助)。
|
||||
internal func removeConnId(list: ArrayList<Int64>, id: Int64): Unit {
|
||||
var i: Int64 = 0
|
||||
while (i < list.size) {
|
||||
if (list[i] == id) {
|
||||
list.remove(i..(i + 1))
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 组门面:由 WebSocketServer.group(name) 返回,支持链式 join/leave/send(send 三重重载:WebSocketMessage / String / Array<Byte>)与 count/isEmpty/connIds。
|
||||
public class WebSocketGroupSender {
|
||||
private let server: WebSocketServer
|
||||
private let group: String
|
||||
|
||||
internal init(server: WebSocketServer, group: String) {
|
||||
this.server = server
|
||||
this.group = group
|
||||
}
|
||||
|
||||
/// 向组内所有连接发送消息(链式;组/连接不存在或发送失败均静默忽略)。
|
||||
public func send(message: WebSocketMessage): WebSocketGroupSender {
|
||||
for (id in connIds()) {
|
||||
if (let Some(c) <- server.getConn(id)) {
|
||||
try {
|
||||
c.send(message)
|
||||
} catch (_) {
|
||||
()
|
||||
}
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// 向组内所有连接发送文本(链式)。
|
||||
public func send(text: String): WebSocketGroupSender {
|
||||
send(WebSocketMessage.fromText(text))
|
||||
}
|
||||
|
||||
/// 向组内所有连接发送二进制(链式)。
|
||||
public func send(data: Array<Byte>): WebSocketGroupSender {
|
||||
send(WebSocketMessage.fromBinary(data))
|
||||
}
|
||||
|
||||
/// 将连接加入组(组不存在则自动创建;链式)。
|
||||
public func join(conn: WebSocketConnection): WebSocketGroupSender {
|
||||
join(conn.connId)
|
||||
}
|
||||
|
||||
/// 将连接加入组(按连接Id;链式)。
|
||||
public func join(connId: Int64): WebSocketGroupSender {
|
||||
synchronized(server.connLock) {
|
||||
if (let Some(l) <- server.connGroups.get(group)) {
|
||||
if (!listHasConnId(l, connId)) {
|
||||
l.add(connId)
|
||||
}
|
||||
} else {
|
||||
let l = ArrayList<Int64>()
|
||||
l.add(connId)
|
||||
server.connGroups[group] = l
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// 将连接移出组(组为空则删除组;链式)。
|
||||
public func leave(conn: WebSocketConnection): WebSocketGroupSender {
|
||||
leave(conn.connId)
|
||||
}
|
||||
|
||||
/// 将连接移出组(按连接Id;链式)。
|
||||
public func leave(connId: Int64): WebSocketGroupSender {
|
||||
synchronized(server.connLock) {
|
||||
if (let Some(l) <- server.connGroups.get(group)) {
|
||||
removeConnId(l, connId)
|
||||
if (l.size == 0) {
|
||||
server.connGroups.remove(group)
|
||||
}
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// 组内连接数。
|
||||
public func count(): Int64 {
|
||||
synchronized(server.connLock) {
|
||||
if (let Some(l) <- server.connGroups.get(group)) {
|
||||
l.size
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 组是否为空。
|
||||
public func isEmpty(): Bool {
|
||||
count() == 0
|
||||
}
|
||||
|
||||
/// 组内所有连接Id(快照)。
|
||||
public func connIds(): ArrayList<Int64> {
|
||||
synchronized(server.connLock) {
|
||||
let copy = ArrayList<Int64>()
|
||||
if (let Some(l) <- server.connGroups.get(group)) {
|
||||
for (x in l) {
|
||||
copy.add(x)
|
||||
}
|
||||
}
|
||||
copy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 连接门面:由 WebSocketServer.conn(connId) 返回,支持链式 send(三重重载:WebSocketMessage / String / Array<Byte>)与 groups()。
|
||||
public class WebSocketConnSender {
|
||||
private let server: WebSocketServer
|
||||
private let connId: Int64
|
||||
|
||||
internal init(server: WebSocketServer, connId: Int64) {
|
||||
this.server = server
|
||||
this.connId = connId
|
||||
}
|
||||
|
||||
/// 向指定连接发送消息(链式;连接不存在或发送失败均静默忽略)。
|
||||
public func send(message: WebSocketMessage): WebSocketConnSender {
|
||||
if (let Some(c) <- server.getConn(connId)) {
|
||||
try {
|
||||
c.send(message)
|
||||
} catch (_) {
|
||||
()
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// 向指定连接发送文本(链式)。
|
||||
public func send(text: String): WebSocketConnSender {
|
||||
send(WebSocketMessage.fromText(text))
|
||||
}
|
||||
|
||||
/// 向指定连接发送二进制(链式)。
|
||||
public func send(data: Array<Byte>): WebSocketConnSender {
|
||||
send(WebSocketMessage.fromBinary(data))
|
||||
}
|
||||
|
||||
/// 连接所属的所有组名(快照)。
|
||||
public func groups(): ArrayList<String> {
|
||||
synchronized(server.connLock) {
|
||||
let res = ArrayList<String>()
|
||||
for ((k, v) in server.connGroups) {
|
||||
if (listHasConnId(v, connId)) {
|
||||
res.add(k)
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
/*
|
||||
* simcu::websocket.server —— 事件回调式 WebSocket 服务端(RFC 6455)。
|
||||
* simcu::websocket.server —— WebSocket 服务端(RFC 6455),handler 接口式。
|
||||
*
|
||||
* 用法:
|
||||
* let server = WebSocketServer(bindAt: 8080)
|
||||
* server.on("connection", { (conn) =>
|
||||
* println("新连接: ${conn.remoteAddress}")
|
||||
* conn.on("message", { (msg) => conn.sendText("echo: ${msg.text}") })
|
||||
* })
|
||||
* server.listen()
|
||||
* let server = WebSocketServer(8080)
|
||||
* server.addHandler("/ws") { => MyHandler() } // 路径 -> 工厂,每次握手创建独立 handler 实例
|
||||
* server.start()
|
||||
* ...
|
||||
* server.close() // 停止监听并断开所有连接
|
||||
*
|
||||
* 服务端事件:on('connection') / on('message') / on('close') / on('error')。
|
||||
* 连接方法:send / sendText / sendBinary / ping / close / terminate。
|
||||
* 集群方法:broadcast / broadcastText / broadcastBinary / connectionCount。
|
||||
* 多 handler 按注册路径路由:握手时按请求路径匹配 addHandler 的 path,匹配则调用工厂
|
||||
* 创建一个新的 handler 实例(每连接一个),无匹配返回 404。
|
||||
* 连接方法:send / ping / close / terminate(send 三重重载:WebSocketMessage / String / Array<Byte>)。
|
||||
* 组管理(链式):ws.group(name).join/leave/count/isEmpty/connIds,ws.conn(connId).groups(),ws.groups()。
|
||||
* 发送门面(链式):group(name).send / conn(connId).send(均为三重重载)。
|
||||
* 广播:broadcast(三重重载)/ connectionCount。
|
||||
*/
|
||||
package simcu::websocket.server
|
||||
|
||||
import std.collection.ArrayList
|
||||
import std.collection.{ArrayList, HashMap}
|
||||
import std.net.IPSocketAddress
|
||||
import std.net.SocketException
|
||||
import std.net.TcpServerSocket
|
||||
@@ -25,78 +25,37 @@ import std.net.TcpSocket
|
||||
import std.sync.Mutex
|
||||
|
||||
import simcu::websocket.common.FrameStream
|
||||
import simcu::websocket.common.MessageType
|
||||
import simcu::websocket.common.WebSocketMessage
|
||||
import simcu::websocket.common.WebSocketException
|
||||
import simcu::websocket.common.WsEvents
|
||||
|
||||
/// 事件回调式 WebSocket 服务端。
|
||||
/// WebSocket 服务端(按路径工厂路由:每连接创建独立 handler 实例,支持组管理/单播/组播/广播)。
|
||||
public class WebSocketServer {
|
||||
private let bindPort: UInt16
|
||||
private let pathFilter: ?String
|
||||
private let maxPayload: Int64
|
||||
private var pathFactories = HashMap<String, () -> IWebsocketHandler>()
|
||||
private var serverSock: ?TcpServerSocket = None
|
||||
private var running = false
|
||||
private var connections = ArrayList<WebSocketConnection>()
|
||||
private var connections = HashMap<Int64, WebSocketConnection>()
|
||||
internal var connGroups = HashMap<String, ArrayList<Int64>>()
|
||||
private var nextConnId: Int64 = 1
|
||||
private let connLock = Mutex()
|
||||
internal let connLock = Mutex()
|
||||
|
||||
/// 服务端事件:有连接完成握手时触发(参数为连接对象)。
|
||||
public var onConnection: (WebSocketConnection) -> Unit = { conn => () }
|
||||
/// 服务端事件:任意连接收到消息时触发(参数为连接对象与消息)。
|
||||
public var onMessage: (WebSocketConnection, WebSocketMessage) -> Unit = { conn, msg => () }
|
||||
/// 服务端事件:任意连接关闭时触发(参数为连接对象、关闭码、原因)。
|
||||
public var onClose: (WebSocketConnection, Int64, String) -> Unit = { conn, code, reason => () }
|
||||
/// 服务端事件:监听或连接运行时错误时触发(参数为异常)。
|
||||
public var onError: (Exception) -> Unit = { e => () }
|
||||
|
||||
/// @param bindAt 监听端口,0 表示随机空闲端口(listen 后通过 localPort 读取)。
|
||||
/// @param path 仅接受该路径的握手请求(None 表示不限制)。
|
||||
/// @param port 监听端口,0 表示随机空闲端口(start 后通过 localPort 读取)。
|
||||
/// @param maxPayload 单条消息最大字节数,默认 64KB。
|
||||
public init(bindAt!: UInt16 = 0, path!: ?String = None, maxPayload!: Int64 = 65536) {
|
||||
this.bindPort = bindAt
|
||||
this.pathFilter = path
|
||||
public init(port: Int64, maxPayload!: Int64 = 65536) {
|
||||
this.bindPort = UInt16(port)
|
||||
this.maxPayload = maxPayload
|
||||
}
|
||||
|
||||
/// 注册服务端事件。支持 "connection"。
|
||||
public func on(event: String, listener: (WebSocketConnection) -> Unit): Unit {
|
||||
if (event == WsEvents.connection) {
|
||||
onConnection = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册服务端事件。支持 "message"。
|
||||
public func on(event: String, listener: (WebSocketConnection, WebSocketMessage) -> Unit): Unit {
|
||||
if (event == WsEvents.message) {
|
||||
onMessage = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册服务端事件。支持 "close"。
|
||||
public func on(event: String, listener: (WebSocketConnection, Int64, String) -> Unit): Unit {
|
||||
if (event == WsEvents.close) {
|
||||
onClose = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
}
|
||||
|
||||
/// 注册服务端事件。支持 "error"。
|
||||
public func on(event: String, listener: (Exception) -> Unit): Unit {
|
||||
if (event == WsEvents.error) {
|
||||
onError = listener
|
||||
} else {
|
||||
throw WebSocketException("不支持的事件: ${event}")
|
||||
}
|
||||
/// 注册一个 handler 工厂并绑定到端点路径(多 handler 按路径路由)。
|
||||
/// 每次握手成功都会调用工厂创建一个新的 handler 实例(每连接一个),
|
||||
/// 因此不要在工厂里复用有状态对象(如 scoped 服务),应让各回调按需现场解析。
|
||||
public func addHandler(path: String, factory: () -> IWebsocketHandler): Unit {
|
||||
pathFactories[path] = factory
|
||||
}
|
||||
|
||||
/// 启动监听:bind + accept 循环,每连接一个协程处理握手与读循环。
|
||||
public func listen(backlog!: Int64 = 128): Unit {
|
||||
public func start(backlog!: Int64 = 128): Unit {
|
||||
if (running) {
|
||||
throw WebSocketException("服务端已在监听")
|
||||
}
|
||||
@@ -129,7 +88,7 @@ public class WebSocketServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止监听并强制断开所有连接(每连接会触发自身 close 事件)。
|
||||
/// 停止监听并强制断开所有连接(每连接会触发自身 close 回调)。
|
||||
public func close(): Unit {
|
||||
running = false
|
||||
if (let Some(s) <- serverSock) {
|
||||
@@ -158,33 +117,44 @@ public class WebSocketServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// 广播文本消息。
|
||||
public func broadcastText(text: String): Unit {
|
||||
/// 广播文本消息(三重重载:WebSocketMessage / String / Array<Byte>)。
|
||||
public func broadcast(text: String): Unit {
|
||||
broadcast(WebSocketMessage.fromText(text))
|
||||
}
|
||||
|
||||
/// 广播二进制消息。
|
||||
public func broadcastBinary(data: Array<Byte>): Unit {
|
||||
public func broadcast(data: Array<Byte>): Unit {
|
||||
broadcast(WebSocketMessage.fromBinary(data))
|
||||
}
|
||||
|
||||
// ===== 内部回调转发(供 WebSocketConnection 调用)=====
|
||||
|
||||
internal func fireMessage(conn: WebSocketConnection, msg: WebSocketMessage): Unit {
|
||||
onMessage(conn, msg)
|
||||
/// 所有组名(快照)。
|
||||
public func groups(): ArrayList<String> {
|
||||
synchronized(connLock) {
|
||||
let copy = ArrayList<String>()
|
||||
for ((k, _) in connGroups) {
|
||||
copy.add(k)
|
||||
}
|
||||
copy
|
||||
}
|
||||
}
|
||||
|
||||
internal func fireClose(conn: WebSocketConnection, code: Int64, reason: String): Unit {
|
||||
onClose(conn, code, reason)
|
||||
// ===== 链式发送门面 =====
|
||||
|
||||
/// 组门面(链式): ws.group("authed").join(conn).send("hi").send(data);count/isEmpty/connIds 读组信息。
|
||||
public func group(name: String): WebSocketGroupSender {
|
||||
WebSocketGroupSender(this, name)
|
||||
}
|
||||
|
||||
internal func fireError(e: Exception): Unit {
|
||||
onError(e)
|
||||
/// 连接门面(链式): ws.conn(connId).send("hi").send(data);groups() 查连接所属组。
|
||||
public func conn(connId: Int64): WebSocketConnSender {
|
||||
WebSocketConnSender(this, connId)
|
||||
}
|
||||
|
||||
// ===== 内部连接管理 =====
|
||||
|
||||
internal func addConnection(conn: WebSocketConnection): Unit {
|
||||
synchronized(connLock) {
|
||||
connections.add(conn)
|
||||
connections[conn.connId] = conn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,23 +168,36 @@ public class WebSocketServer {
|
||||
|
||||
internal func removeConnection(conn: WebSocketConnection): Unit {
|
||||
synchronized(connLock) {
|
||||
var idx: Int64 = -1
|
||||
for (i in 0..connections.size) {
|
||||
if (connections[i].connId == conn.connId) {
|
||||
idx = i
|
||||
break
|
||||
connections.remove(conn.connId)
|
||||
removeFromAllGroups(conn.connId)
|
||||
}
|
||||
}
|
||||
|
||||
internal func getConn(connId: Int64): ?WebSocketConnection {
|
||||
synchronized(connLock) {
|
||||
connections.get(connId)
|
||||
}
|
||||
}
|
||||
|
||||
private func removeFromAllGroups(connId: Int64): Unit {
|
||||
var toRemove = ArrayList<String>()
|
||||
for ((g, list) in connGroups) {
|
||||
if (listHasConnId(list, connId)) {
|
||||
removeConnId(list, connId)
|
||||
if (list.size == 0) {
|
||||
toRemove.add(g)
|
||||
}
|
||||
}
|
||||
if (idx >= 0) {
|
||||
connections.remove(at: idx)
|
||||
}
|
||||
}
|
||||
for (g in toRemove) {
|
||||
connGroups.remove(g)
|
||||
}
|
||||
}
|
||||
|
||||
private func snapshotConnections(): ArrayList<WebSocketConnection> {
|
||||
synchronized(connLock) {
|
||||
let copy = ArrayList<WebSocketConnection>()
|
||||
for (c in connections) {
|
||||
for (c in connections.values()) {
|
||||
copy.add(c)
|
||||
}
|
||||
copy
|
||||
@@ -223,6 +206,11 @@ public class WebSocketServer {
|
||||
|
||||
// ===== 监听与握手 =====
|
||||
|
||||
/// 按请求路径匹配 handler 工厂。无匹配返回 None。
|
||||
private func findFactory(path: String): ?() -> IWebsocketHandler {
|
||||
pathFactories.get(path)
|
||||
}
|
||||
|
||||
private func acceptLoop(): Unit {
|
||||
while (running) {
|
||||
try {
|
||||
@@ -237,12 +225,12 @@ public class WebSocketServer {
|
||||
if (!running) {
|
||||
return
|
||||
}
|
||||
onError(e)
|
||||
println("[websocket] accept 失败: ${e}")
|
||||
} catch (e: Exception) {
|
||||
if (!running) {
|
||||
return
|
||||
}
|
||||
onError(e)
|
||||
println("[websocket] accept 失败: ${e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,11 +249,19 @@ public class WebSocketServer {
|
||||
reject(sock, 400, "Bad Request")
|
||||
return
|
||||
}
|
||||
if (let Some(p) <- pathFilter) {
|
||||
if (parts[1] != p) {
|
||||
reject(sock, 404, "Not Found")
|
||||
return
|
||||
}
|
||||
// 拆出路径与 query(如 "/ws/server?serverId=srv-1" → path="/ws/server", query="serverId=srv-1")
|
||||
let target = parts[1]
|
||||
var requestPath = target
|
||||
var requestQuery = ""
|
||||
if (let Some(qi) <- target.indexOf("?")) {
|
||||
requestPath = target[0..qi]
|
||||
requestQuery = target[qi + 1..]
|
||||
}
|
||||
// 按 WsPath 路由到 handler 工厂,匹配则调用工厂创建新的 handler 实例(每连接一个)
|
||||
let factory = findFactory(requestPath)
|
||||
if (let None <- factory) {
|
||||
reject(sock, 404, "Not Found")
|
||||
return
|
||||
}
|
||||
var upgradeOk = false
|
||||
var connectionOk = false
|
||||
@@ -302,13 +298,15 @@ public class WebSocketServer {
|
||||
sock.write(resp.toString().toArray())
|
||||
sock.flush()
|
||||
|
||||
let conn = WebSocketConnection(Some(this), fs, allocateConnId())
|
||||
let fac = factory.getOrThrow()
|
||||
let h = fac()
|
||||
let conn = WebSocketConnection(Some(this), h, fs, allocateConnId(), requestPath, requestQuery)
|
||||
addConnection(conn)
|
||||
onConnection(conn)
|
||||
h.onConnect(conn)
|
||||
conn.runReadLoop()
|
||||
} catch (e: Exception) {
|
||||
// 握手阶段失败:上报服务端 error 事件
|
||||
onError(e)
|
||||
// 握手阶段失败:记录日志(连接尚未建立,无 handler 可回调)
|
||||
println("[websocket] 握手失败: ${e}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user