Kotlin中的WebSocket通信
WebSocket通信是非常常见的,基于TCP通信的一种方式。接下来将了解集中,在kotlin中实现WebSocket通信的方案
Java-WebSocket
Java-WebSocket是一个独立于Spring体系之外,可以独立运行的WebSocket构建工具。它包含了服务端与客户端。
- 服务端:提供
WebSocket的链接服务,处理消息,回复消息,广播消息,实现消息中转。一个服务端可以被多个客户端链接。 - 客户端:链接服务端的客户端,连接到服务到之后可以接收和处理来自服务端的消息,基于服务端做实时通讯。
要使用Java-WebSocket,需要引入以下依赖:
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.6.0</version>
</dependency>
基础构建
如何构建一个服务端
import org.java_websocket.WebSocket
import org.java_websocket.handshake.ClientHandshake
import org.java_websocket.server.WebSocketServer
import java.lang.Exception
import java.net.InetSocketAddress
class VideoSocketServer(port: Int) : WebSocketServer(InetSocketAddress(port)) {
override fun onOpen(conn: WebSocket, handshake: ClientHandshake) {
// 获取连接头内容
println("新客户端连接:${conn.remoteSocketAddress}")
}
override fun onClose(conn: WebSocket, code: Int, reason: String, remote: Boolean) {
println("客户端断开:${closeCoon.remoteSocketAddress}")
}
override fun onMessage(conn: WebSocket, message: String) {
}
override fun onError(conn: WebSocket?, ex: Exception) {
ex.printStackTrace()
}
override fun onStart() {
println("WebSocket 服务器已启动")
}
}
如上就构建了一个WebSocket服务端,服务端的构建需要继承WebSocketServer抽象类,接下来分析上述API
| 名称 | 作用 |
|---|---|
open |
当有客户端连接该服务端时触发 |
onClose |
当有客户端从服务端断连时触发 |
onMessage |
接受来自服务端的消息,onMessage有多种实现,可以接收文字消息,字节流消息,根据不同场景进行实现即可。消息发发送时,Java-WebSocket会自动选择适合的方法接收 |
onError |
服务发生异常时触发 |
onStart |
服务端启动时触发 |
通过ClientHandshake提供的getFieldValue()方法可以获取来自客服端请求的Header |
如何启动这个服务端
fun main() {
val videoSocketServer = VideoSocketServer(8333)
videoSocketServer.start()
}
如上,这个服务端就启动了,并且会自动产生阻塞,除非服务端停止
如何构建一个客户端
class VideoSendClient(uri: URI, header: HashMap<String, String>) : WebSocketClient(uri, header) {
override fun onOpen(handshake: ServerHandshake) {
println("客户端链接成功....")
}
override fun onMessage(message: String) {}
// 处理流
override fun onMessage(bytes: ByteBuffer) {
}
override fun onClose(code: Int, reason: String, remote: Boolean) {
println("客户端链接关闭")
}
override fun onError(e: Exception) {
}}
以上代码构建了一个客户端,需要继承WebSocketClient抽象类,WebSocketClient抽象类构造时还可以支持传入指定的header,也传入header
以下是一个客户端并添加header的示例
fun main() {
// 初始化连接头,用以表示信息
private val header = HashMap<String, String>().also {
val readConfigProperty = ReadYamlUtils.readConfigProperty("send") as Map<*, *>
it["name"] = readConfigProperty["name"].toString()
it["type"] = "send"
it["receive-client-name"] = readConfigProperty["receive-client-name"].toString()
}
socketServer = VideoSendClient(URI("ws://localhost:8333"), header)
socketServer.connect()
socketServer.send("this is a message")
}
双向通信
使用Java-WebSocket简历一个A客户端发送消息B客户端接受的Demo
首先准备一个SocketClient对象,用于存放各个客户端的信息
import org.java_websocket.WebSocket
class SocketClient(
// 客户端/服务端名称
var name: String,
// send/receive
var type: String,
// 接收端名称/发送端名称
var direction: String,
// socket链接服务
var socket: WebSocket
)
接下来准备一个WebSocket服务端,用于转发发送方到接收方
class VideoSocketServer(port: Int) : WebSocketServer(InetSocketAddress(port)) {
companion object {
// 准备两个集合,接收与发送方
val CONN_LIST = CopyOnWriteArrayList<SocketClient>()
}
override fun onOpen(conn: WebSocket, handshake: ClientHandshake) {
// 获取连接头内容
val name = handshake.getFieldValue("name")
val type = handshake.getFieldValue("type")
val direction = handshake.getFieldValue("direction")
CONN_LIST.add(SocketClient(name, type, direction, conn))
println("新客户端连接:${conn.remoteSocketAddress},name=$name,type=$type,direction=$direction")
}
override fun onClose(conn: WebSocket, code: Int, reason: String, remote: Boolean) {
println("客户端断开连接:${conn.remoteSocketAddress},code=$code,reason=$reason")
CONN_LIST.removeIf { it.socket == conn }
}
override fun onMessage(conn: WebSocket, message: String) {
// 判断消息是否来自发送方
val send = CONN_LIST.stream().filter { it.socket == conn && it.type == "send" }.findFirst().orElse(null)
println("接收到客户端消息:${conn.remoteSocketAddress},message=$message,from: name=${send.name},type=${send.type},direction=${send.direction}")
// 如果发送方已注册到集合中,获取其接收链接并向其发送消息
if (send != null) {
val receive =
CONN_LIST.stream().filter { it.direction == send.name && it.type == "receive" }.findFirst().orElse(null)
receive?.socket?.send(message)
}
}
override fun onError(conn: WebSocket?, ex: Exception) {
ex.printStackTrace()
}
override fun onStart() {
println("WebSocket 服务器已启动")
}
}
// 服务端启动代码
fun main() {
val videoSocketServer = VideoSocketServer(8333)
videoSocketServer.start()
}
上面的服务端代码中,所有的必要信息通过Header发送到服务端接收,并转换为对象存储在集合中
接下来准备发送端代码
class VideoSendClient(uri: URI, header: HashMap<String, String>) : WebSocketClient(uri, header) {
override fun onOpen(handshake: ServerHandshake) {
println("客户端链接成功,开始发送视频画面....")
this.send("this is a message")
}
override fun onMessage(message: String) {}
override fun onMessage(bytes: ByteBuffer) {
}
override fun onClose(code: Int, reason: String, remote: Boolean) {
println("客户端链接关闭")
}
override fun onError(e: Exception) {
}}
// 发送端启动代码
fun main() {
val header = HashMap<String, String>().also {
val readConfigProperty = ReadYamlUtils.readConfigProperty("send") as Map<*, *>
it["name"] = readConfigProperty["name"].toString()
it["type"] = "send"
it["direction"] = readConfigProperty["direction"].toString()
}
val socketServer = VideoSendClient(URI("ws://localhost:8333"), header)
socketServer.connect()
}
这里添加了一个测试消息,当消息建立时立即发送一条消息到服务端以验证服务是否成功
然后准备接受方服务
import javafx.scene.image.ImageView
import org.java_websocket.client.WebSocketClient
import org.java_websocket.handshake.ServerHandshake
import java.net.URI
import java.nio.ByteBuffer
class VideoReceiveClient(uri: URI, header: HashMap<String, String>) :
WebSocketClient(uri, header) {
override fun onOpen(handshake: ServerHandshake) {
println("客户端链接成功,开始渲染视频画面....")
}
override fun onMessage(message: String) {
println("客户端接收到消息:$message")
}
// 处理流
override fun onMessage(bytes: ByteBuffer) {
}
override fun onClose(code: Int, reason: String, remote: Boolean) {
println("客户端链接关闭")
}
override fun onError(e: Exception) {
}}
// 接收端启动代码
fun main() {
val header = HashMap<String, String>().also {
val readConfigProperty = ReadYamlUtils.readConfigProperty("receive") as Map<*, *>
it["name"] = readConfigProperty["name"].toString()
it["type"] = "receive"
it["direction"] = readConfigProperty["direction"].toString()
}
val socketServer = VideoSendClient(URI("ws://localhost:8333"), header)
socketServer.connect()
}
接收方收到消息,之后会立马打印

浙公网安备 33010602011771号