移动端开发网络/Web

Socket搭建即时通讯服务器

2018-11-16  本文已影响356人  TitanCoder
webSecket

即时通讯

IM通信原理

即时通讯连接原理

即时通讯数据传递方式

目前实现即时通讯的有四种方式(短轮询、长轮询、SSE、Websocket

短轮询:

长轮询:

SSE

WebSocket

WebSocket

WebSocket如何工作

webServer

Websocket协议

协议头: ws, 服务器根据协议头判断是Http还是websocket

// 请求头
     GET ws://localhost:12345/websocket/test.html HTTP/1.1
     Origin: http://localhost
     Connection: Upgrade
     Host: localhost:12345
     Sec-WebSocket-Key: JspZdPxs9MrWCt3j6h7KdQ==  
     Upgrade: websocket 
     Sec-WebSocket-Version: 13
    // Sec-WebSocket-Key: 叫“梦幻字符串”是个密钥,只有有这个密钥 服务器才能通过解码认出来,这是个WB的请求,要建立TCP连接了!!!如果这个字符串没有按照加密规则加密,那服务端就认不出来,就会认为这整个协议就是个HTTP请求。更不会开TCP。其他的字段都可以随便设置,但是这个字段是最重要的字段,标识WB协议的一个字段
     

// 响应头
     HTTP/1.1 101 Web Socket Protocol Handshake
     WebSocket-Location: ws://localhost:12345/websocket/test.php
     Connection: Upgrade
     Upgrade: websocket
     Sec-WebSocket-Accept: zUyzbJdkVJjhhu8KiAUCDmHtY/o= 
     WebSocket-Origin: http://localhost
     
    // Sec-WebSocket-Accept: 叫“梦幻字符串”,和上面那个梦幻字符串作用一样。不同的是,这个字符串是要让客户端辨认的,客户端拿到后自动解码。并且辨认是不是一个WB请求。然后进行相应的操作。这个字段也是重中之重,不可随便修改的。加密规则,依然是有规则的

WebSocket客户端

在客户端,没有必要为WebSockets使用JavaScript库。实现WebSocketsWeb 浏览器将通过WebSockets对象公开所有必需的客户端功能(主要指支持HTML5的浏览器)

客户端 API

以下 API 用于创建WebSocket对象。

var Socket = new WebSocket(url, [protocol] );

WebSocket属性

以下是WebSocket对象的属性。假定我们使用了以上代码创建了Socket对象

WebSocket事件

以下是WebSocket对象的相关事件。假定我们使用了以上代码创建了Socket 对象:

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

WebSocket方法

以下是WebSocket对象的相关方法。假定我们使用了以上代码创建了Socket对象:

方法 描述
Socket.send() 使用连接发送数据
Socket.close() 关闭连接

示例

// 客户端
var socket = new WebSocket("ws://localhost:9090")

// 建立 web socket 连接成功触发事件
socket.onopen = function () {
    // 使用send发送数据
    socket.send("发送数据")
    console.log(socket.bufferedAmount)
    alert('数据发送中')
}

// 接受服务端数据是触发事件
socket.onmessage = function (evt) {
    var received_msg = evt.data
    alert('数据已经接受..')
}

// 断开 websocket 连接成功触发事件
socket.onclose = function () {
    alert('链接已经关闭')
    console.log(socket.readyState)
}

WebSocket服务端

WebSocket在服务端的实现非常丰富。Node.jsJavaC++Python 等多种语言都有自己的解决方案, 其中Node.js常用的有以下三种

下面就着重研究一下Socket.IO吧, 因为别的我也不会, 哈哈哈哈......

Socket.IO

Socket.IO服务端

导入Socket.IO

// 1. 进入当当前文件夹
cd ...

// 2. 创建package.json文件
npm init

/// 3. 导入库
npm install socket.io --sava
npm install express --sava

创建socket

// 引入http模块
var http = require('http')

// 面向express框架开发,加载express框架,方便处理get,post请求
var express = require('express')

// 创建web服务器
var server = http.Server(express)

// 引入socket.io模块
var socketio = require('socket.io')

// 创建爱你socket服务器
var serverSocket = socketio(server)


server.listen(9090)
console.log('监听9090')

建立socket连接

// 监听客户端有没有连接成功,如果连接成功,服务端会发送connection事件,通知客户端连接成功
// serverSocket: 服务端, clientSocket: 客户端
serverSocket.on('connection', function (clientSocket) {
    // 建立socket连接成功
    console.log('建立连接成功')

    console.log(clientSocket)
})

Socket.IO客户端

创建socket对象

创建SocketIOClient对象, 两种创建方式

// 第一种, SocketIOClientConfiguration: 可选参数
public init(socketURL: URL, config: SocketIOClientConfiguration = [])

// 第二种, 底层还是使用的第一种方式创建
public convenience init(socketURL: URL, config: [String: Any]?) {
        self.init(socketURL: socketURL, config: config?.toSocketConfiguration() ?? [])
}
public enum SocketIOClientOption : ClientOption {
    /// 使用压缩的方式进行传输
    case compress
    /// 通过字典内容连接
    case connectParams([String: Any])
    /// NSHTTPCookies的数组, 在握手过程中传递, Default is nil.
    case cookies([HTTPCookie])
    /// 添加自定义请求头初始化来请求, 默认为nil
    case extraHeaders([String: String])
    /// 将为每个连接创建一个新的connect, 如果你在重新连接有bug时使用.
    case forceNew(Bool)
    /// 传输是否使用HTTP长轮询, 默认false
    case forcePolling(Bool)
    /// 是否使用 WebSockets. Default is `false`
    case forceWebsockets(Bool)
    /// 调度handle的运行队列, 默认在主队列
    case handleQueue(DispatchQueue)
    /// 是否打印调试信息. Default is false
    case log(Bool)
    /// 可自定义SocketLogger调试日志
    case logger(SocketLogger)
    /// 自定义服务器使用的路径.
    case path(String)
    /// 链接失败时, 是否重新链接, Default is `true`
    case reconnects(Bool)
    /// 重新连接多少次. Default is `-1` (无限次)
    case reconnectAttempts(Int)
    /// 等待重连时间. Default is `10`
    case reconnectWait(Int)
    /// 是否使用安全传输, Default is false
    case secure(Bool)
    /// 设置允许那些证书有效
    case security(SSLSecurity)
    /// 自签名只能用于开发模式
    case selfSigned(Bool)
    /// NSURLSessionDelegate 底层引擎设置. 如果你需要处理自签名证书. Default is nil.
    case sessionDelegate(URLSessionDelegate)
}

创建SocketIOClient

// 注意协议:ws开头
guard let url = URL(string: "ws://localhost:9090") else { return }
let manager = SocketManager(socketURL: url, config: [.log(true), .compress])
// SocketIOClient
let socket = manager.defaultSocket

监听连接

// 回调闭包
public typealias NormalCallback = ([Any], SocketAckEmitter) -> ()

// on方法
@discardableResult
open func on(_ event: String, callback: @escaping NormalCallback) -> UUID

// SocketClientEvent: 接受枚举类型的on方法
@discardableResult
open func on(clientEvent event: SocketClientEvent, callback: @escaping NormalCallback) -> UUID {
    // 这里调用的是上面的on方法
    return on(event.rawValue, callback: callback)
}

完整代码

guard let url = URL(string: "ws://localhost:9090") else { return }

let manager = SocketManager(socketURL: url, config: [.log(true), .compress])
let socket = manager.defaultSocket

// 监听链接成功
socket.on(clientEvent: .connect) { (data, ack) in
    print("链接成功")
    print(data)
    print(ack)
}
        
socket.connect()

SocketIO事件

SocketIO通过事件链接服务器和传递数据

客户端监听事件

// 监听链接成功
socket.on(clientEvent: .connect) { (data, ack) in
    print("链接成功")
    print(data)
    print(ack)
}

客户端发送事件

只有连接成功之后,才能发送事件

// 建立一个连接到服务器. 连接成功会触发 "connect"事件
open func connect()

// 连接到服务器. 如果连接超时,会调用handle
open func connect(timeoutAfter: Double, withHandler handler: (() -> ())?)

// 重开一个断开连接的socket
open func disconnect()

// 向服务器发送事件, 参数一: 事件的名称,参数二: 传输的数据组
open func emit(_ event: String, with items: [Any])

服务器监听事件

// 监听socket连接
socket.on('connection',function(s){

    console.log('监听到客户端连接');

    // data:客户端数组第0个元素
    // data1:客户端数组第1个元素
    s.on('chat',function(data,data1){

        console.log('监听到chat事件');

        console.log(data,data1);
        
    });
});

服务器发送事件

这里的socket一定要用服务器端的socket

// 给当前客户端发送数据,其他客户端收不到.
socket.emit('chat', '服务器' + data)

// 发给所有客户端,不包含当前客户端
socket.emit.broadcast.emit('chat', '发给所有客户端,不包含当前客户端' + data)

// 发给所有客户端,包含当前客户端
socket.emit.sockets.emit('chat', '发给所有客户端,包含当前客户端' + data)

SocketIO分组

如何分组

// join和leave
io.on('connection', function(socket){
  socket.join('some room');
  // socket.leave('some room');
});
 
// say to room
io.to('some room').emit('some event'):
io.in('some room').emit('some event'):

分组的原理

上一篇 下一篇

猜你喜欢

热点阅读