WebSocket 模块
CBrother在1.0.7版本以后提供了WebSocket支持,包含在Http扩展中,既可以当服务器,也可以当客户端。
WebSocket可以说是HTTP服务与TCP服务的合体,当由HTTP服务器升级完成协议后交由TCP模块接管。在使用之前你需要先了解HttpServer和TcpModule的相关用法。
WebSocketModule
WebSocketModule底层是TcpModule,所以WebSocketModule是TcpModule的超集。
·WebSocketModule接口
函数 | 描述 | 用法 |
---|---|---|
setWebSocketAction(actobj) | 设置响应接口 | wsModule.setWebSocketAction(actobj) |
start() | 启动服务,返回true为启动成功 | wsModule.start() |
stop() | 停止服务 | wsModule.stop() |
setThreadCount(cnt) | 设置工作线程数量 默认5个 可以大到CPU核数3 但是消耗资源增多 | wsModule.setThreadCount(8) |
setMaxConnectCount(cnt) | 设置最大管理的连接数量,根据需求设置 过大了开销资源很大 默认2048 | wsModule.setMaxConnectCount(5000) |
setSockNormalBuflLen(recvLen,sendLen) | 设置socket接收缓冲和发送缓冲默认值,默认4096 | wsModule.setSockNormalBuflLen(1024\10,1024*15) |
connect(req) | 主动连接某TCP服务器,返回socketid,-1为失败,参数是一个HttpClientRequest对象 | wsModule.connect(req) |
sendData(socketid,type,str,ismask) | 发送字符串数据,socketid:socket标示 type:消息类型(WS_MSG_TEXT) str:字符串 ismask为是否异或加密,默认不加密,返回true为成功 | wsModule.sendData(socketid,WS_MSG_TEXT,"111") |
sendData(socketid,type,bytearray,len,ismask) | 发送二进制,socketid:socket标示 type:消息类型(WS_MSG_BINARY) bytearray:二进制数据 len:发送长度 ismask为是否异或加密,默认不加密,返回true为成功 | wsModule.sendData(socketid,WS_MSG_BINARY,byteArray,10) |
closeSocket(socketid) | 关闭socket,返回true为成功 | wsModule.closeSocket(socketid) |
getRemoteIP(socketid) | 获取socket对端IP | var ip = wsModule.getRemoteIP(socketid) |
setSockBuflLen(socketid,recvLen,sendLen) | 设置socketid接收缓冲和发送缓冲大小,socketid:socket标示 recvLen:接收缓冲 sendLen:发送缓冲 不设置用默认的 只能在OnAccept和OnConnect里调用 | wsModule.setSockBuflLen(socketid,10*1024,10,1024) |
getSocketCount() | 获取当前管理的socket数量 | wsModule.getSocketCount() |
setWebSocketAction
,setThreadCount
,setMaxConnectCount
,setSockNormalBuflLen
,要在start
之前调用。
·WebSocketModule 响应Action类可以有如下接口
function OnUpgrade(req,sock,isaccept)
,协议升级成功。
function OnAccept(sock)
,接管新的连接。
function OnClose(sock)
,连接断开。
function OnRecv(socketid,type,data,len)
接收到数据。websocket消息有四种:文本,二进制,ping和pong,定义在lib/wsdef.cb里
function OnSend(sock,len)
发送成功。
function OnConnect(sock)
接管主动发起的连接。
这个方法会被多线程调用,如果要访问公共资源需要加锁。但对于同一个socket而言,OnRecv是串行的。
·WebSocketModule 例子
服务器例子
import CBHttp.code
import lib/wsdef
class WebSocketAction
{
var _webSocketModule;
var _lock = new Lock();
var _userMap = new Map();
function WebSocketAction(wsModule)
{
_webSocketModule = wsModule;
}
function OnUpgrade(req,socketid,isaccept)
{
if(isaccept) //isaccept为true时表示是作为服务器接管客户端,req则为本次请求的request对象,你可以读取它的信息与socketid建立关系,运行在http服务线程内
{
var cookie = req.getCookie(0);
var name = cookie.getValue();
print "onupgrade " + name;
_lock.lock();
_userMap.add(socketid,name);
_lock.unlock();
}
}
function OnAccept(socketid)//接管成功,运行在TcpModule线程内
{
_lock.lock();
var username = _userMap.get(socketid);
if(username != null)
{
print username + " onaccept";
}
_lock.unlock();
}
function OnRecv(socketid,type,data,len)
{
if(type == WS_MSG_TEXT)//文本消息是个字符串
{
_lock.lock();
var username = _userMap.get(socketid);
if(username != null)
{
print "onrecv " + username + " text:" + data;
_webSocketModule.sendData(socketid,WS_MSG_TEXT,"hi " + username + ",I recv your text msg.");
}
_lock.unlock();
}
else if(type == WS_MSG_BINARY)//二进制是个ByteArray对象
{
_lock.lock();
var username = _userMap.get(socketid);
if(username != null)
{
print "onrecv " + username + " binary datalen:" + len + " " + data.readString();
_webSocketModule.sendData(socketid,WS_MSG_TEXT,"hi " + username + ",I recv your binary msg.");
}
_lock.unlock();
}
}
function OnClose(socketid)
{
_lock.lock();
var username = _userMap.get(socketid);
if(username != null)
{
print username + " Onclose!";
_userMap.remove(socketid);
}
_lock.unlock();
}
}
function main(parm)
{
var websocket = new WebSocketModule();
websocket.setWebSocketAction(new WebSocketAction(websocket));
websocket.start();
var httpServer = new HttpServer();
httpServer.setWebSocketModule("wsb.cb",websocket);//将websocket模块交给httpserver,当用户访问ws://xxx.xxx.xxx.xxx/wsb.cb时候进行协议升级操作
httpServer.startServer();
while(1) //主线程不能退出
{
Sleep(1000);
}
}
客户端例子
import CBHttp.code
import lib/wsdef
class WebSocketAction
{
var _webSocketModule;
var _lock = new Lock();
var _userMap = new Map();
function WebSocketAction(wsModule)
{
_webSocketModule = wsModule;
}
function OnUpgrade(req,socketid,isaccept)
{
if(!isaccept)//主动连接的isaccept为false,运行在发起connect的线程里
{
var cookie = req.getCookie(0);
if(cookie != null)
{
_lock.lock();
_userMap.add(socketid,cookie.getValue());
print cookie.getValue() + " onupgrade success!";
_lock.unlock();
}
}
}
function OnConnect(socketid)
{
_lock.lock();
var username = _userMap.get(socketid);
_lock.unlock();
if(username != null)
{
print username + " connect suc!";
if(username == "xiaoming")
{
_webSocketModule.sendData(socketid,WS_MSG_TEXT,"this is " + username + ",I send a text msg.");
}
else
{
var byteary = new ByteArray();
byteary.writeString("this is " + username + ",I send a binary msg.");
_webSocketModule.sendData(socketid,WS_MSG_BINARY,byteary);
}
}
}
function OnRecv(socketid,type,data,len)
{
if(type == WS_MSG_TEXT)
{
_lock.lock();
var username = _userMap.get(socketid);
_lock.unlock();
if(username != null)
{
print data;
}
}
}
function OnClose(socketid)
{
_lock.lock();
var username = _userMap.get(socketid);
if(username != null)
{
print username + " onclose!";
_userMap.remove(socketid);
}
_lock.unlock();
}
}
function main(parm)
{
var websocket = new WebSocketModule();
websocket.setWebSocketAction(new WebSocketAction(websocket));
websocket.start();
var clientReq = new HttpClientRequest();
clientReq.setUrl("ws://127.0.0.1:8000/wsb.cb"); //http替换为ws,https替换为wss
var myReq = new HttpClientRequest();
clientReq.addCookie(new Cookie("name","xiaoming"));
websocket.connect(clientReq); //交给websocket模块连接
clientReq = new HttpClientRequest();
clientReq.setUrl("ws://127.0.0.1:8000/wsb.cb");
var myReq = new HttpClientRequest();
clientReq.addCookie(new Cookie("name","xiaohong"));
websocket.connect(clientReq);
Sleep(3000);//3秒后退出
}
客户端结果如下:
xiaoming onupgrade success!
xiaohong onupgrade success!
xiaoming connect suc!
xiaohong connect suc!
hi xiaoming,I recv your text msg.
hi xiaohong,I recv your binary msg.
服务端结果如下:
onupgrade xiaoming
xiaoming onaccept
onrecv xiaoming text:this is xiaoming,I send a text msg.
onupgrade xiaohong
xiaohong onaccept
onrecv xiaohong binary datalen:38 this is xiaohong,I send a binary msg.
xiaohong Onclose!
xiaoming Onclose!
这里只是为了同时说明客户端与服务器的用法,在实际的开发过程中,websocket服务器往往是直接跟网页通信的,在下载包的sample/websocket/websocket.cb 是一个网页聊天室的例子,更切合实际开发。只是这个例子前端是一个html,需要一些网页开发的知识, 这里就不做讲解了。要将websocket用到网页的实际开发中,还是要研读一下sample中的例子。