更新于 

Socket 实时通信

Socket

Socket概念

Web网络请求一般常用的就是Http和WebSocket,

在游戏中,常用Http请求向服务器传递/获取一些无需实时更新的数据:

  • 加载游戏数据/资源(玩家等级、装备、技能等信息)
  • 向服务器上传数据(分数、游戏进度、玩家当前位置…)

Socket一般用于需要实时更新的一些数据,
进行Socket连接一般需要3个步骤:

  1. 服务器启动监听
    • 服务器端server socket,对指定端口号进行监听
  2. 客户端请求连接
    • 客户端client socket,对指定的ip和端口号发送连接请求
    • 如果目标ip和端口号是服务器端对应的,则进连接继续
  3. 服务器连接确认
    • 响应客户端请求,建立TCP/IP通道

联网游戏一般分为2种:

  • 局域网对战游戏
  • 网络游戏
局域网对战游戏
  • 方式1:玩家充当局域网通信服务器

一般为局域网中,其中一个玩家的主机充当服务器(房主),
其他玩家通过房主进行数据共享(位置、血量等数据),
此时的房主类似于路由器的功能。

房主通过UDP协议向局域网内所有地址广播自己的IP地址和服务端口(公示自己的房间号),
并专门建立一个端口,用于监听玩家的进入(房门端口),
一旦有人与此端口建立起联系(监听到有玩家进入房门),
就立刻为该玩家分配一个专项端口,并建立连接(给新用户分配一个专享通道),
并关闭玩家与房门端口的连接,

这样由玩家的主机充当通信服务器的局域网方式,一般都会对玩家数量进行限制,

  • 方式2:虚拟IP

常见网络在线对战平台,为玩家分发虚拟IP,
从而达到将广域网IP映射到局域网的目的

网络游戏

不需要玩家来充当通信服务器,
而是由运营方提供专门的通信服务器,
可以是这种结构:

  • Web服务器:收发Http请求
  • Socket通信服务器:专注于收发与转发数据,建立玩家间Socket连接
  • 数据存储服务器:用于存储玩家数据
Socket实例

服务器框架:express

安装express-ws:

1
npm i express-ws

添加ws映射路由,创建websocket.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var express = require('express')
var expressWs = require('express-ws')
var router = express.Router()
expressWs(router)

router.ws('/user', (ws, req)=>{
console.log("收到连接请求")
ws.send('来自服务器端推送的消息')
ws.on('message',(msg)=>{
// 业务代码
ws.send(`服务器端收到消息:${msg}`)
})
let timer = setInterval(()=>{
ws.send(`服务器端定时推送消息:${new Date().getTime()}ms`)
}, 1000)
ws.on('close', e => {
clearInterval(timer)
timer = null
clients = clients.filter(client => client !== ws)
})
})

module.exports = router;

修改app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var expressWs = require('express-ws');

var app = express();
expressWs(app) // 注入ws

var webSocketRouter = require('./routes/websocket');

// 映射链接
app.use('/ws_api', webSocketRouter);

process.env.PORT = 8201
module.exports = app;

如果是使用的express脚手架搭建的项目,
需要在/bin/www中加入修改:

1
2
3
4
5
6
var app = require('../app');
var http = require('http');
var server = http.createServer(app);

// 注入
var expressWs = require('express-ws')(app, server)

客户端新建websocket连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class WSConnect extends cc.Component {
start () {
let socket = new WebSocket("ws://127.0.0.1:8201/ws_api/user")
socket.onopen = ()=>{
console.log("与服务器端建立连接")
}
socket.onmessage = (msg)=>{
console.log(msg)
}
socket.onclose = ()=>{
console.log("CLOSE:与服务器连接断开")
}
}
}

常用readyState状态码:

  • 0
    • CONNECTING
    • 正在建立连接,连接没有建立完成
  • 1
    • OPEN
    • 连接已经建立完成,并且可以进行通信
  • 2
    • CLOSING
    • 连接正在关闭
  • 3
    • CLOSED
    • 连接已经关闭或不可用
websocket实现广播
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 存放当前所有连接
let clients = []
router.ws('/user',(ws, req)=>{
clients.push(ws)
ws.on('message',(msg)=> {
broadcast("广播数据")
})
})
function broadcast(data){
clients.forEach(client => {
if(client.readyState === client.OPEN){
client.send(data)
}
})
}