每天看一片代码系列(二):WebSocket-Node

简介

我们都知道,websocket主要是通过在浏览器和服务端建立长连接,继而实现二者的相互数据通信。不同于HTTP的轮询,它不会有大量无效的HTTP消息交换,从而节省了花销。websocket其实就是双通道的TCP连接。

很明显地,整个工作分为两个步骤,即创建连接和发送数据。那么连接是怎么建立的呢?其实只需要在浏览器和服务器端做一个握手的动作就可以了。而这个握手其实还是一个HTTP请求,只是接下来的工作就和HTTP没关系了。

这个HandShake的请求消息大致为:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

而响应的消息大致为:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

HTTP里面的upgrade头部代表要从HTTP协议切换到另一个协议,或者是更换一个不同版本的HTTP。而Sec-WebSocket-Key和Sec-WebSocket-Accept是用来进行验证的字段。后者是前者经过一系列操作计算出来的,用来给客户端判断响应和请求是否匹配。

使用WebSocket-Node

首先我们体验一下“websocket-node"这个库的用法。

其实用这个库就是为了创建一个websocket-server,在app.js中,我们创建即可。

var port = 3001;
var websocketServer = require(‘websocket‘).server;
var http = require(‘http‘);

// 创建httpServer
var httpServer = http.createServer();
httpServer.listen(port, function () {
    console.log(new Date() + " Http Server is listening on " + port);
});

//创建WebSocketServer
var wsServer = new websocketServer({
    httpServer: httpServer
});

wsServer.on(‘request‘, function (request) {
    console.log(new Date() + " connection from origin: " + request.origin + ‘ .‘);
    //建立连接
    var connection = request.accept(‘example‘, request.origin);

    //向客户端发送消息
    connection.sendUTF("Connection from " + request.origin);

    connection.on(‘close‘, function () {
        console.log(connection.remoteAddress + " disconnected.");
    })

    //收消息
    connection.on(‘message‘, function (message) {
        try{
            connection.sendUTF("Server accpected: " + message.utf8Data);
        }catch(e){

        }
    })
})

在浏览器中,就和这个库没什么关系了,直接用浏览器支持的API创建Websocket客户端,收发消息即可。

var wsServer = "ws://localhost:3001";
var ws = new WebSocket(wsServer, ‘example‘);

ws.onopen = function (e) {
    var msg = "hello Server!";
    ws.send(msg) && print(msg);
}

ws.onclose = function (e) {
    var msg = "closed connection";
    print(msg);
}

ws.onmessage = function (e) {
    var msg = "received : " + e.data;
    print(msg);
}

源码解析

源码有多个文件,我们只关注WebSocketServer.js,地址

首先,在传入的配置中必须要绑定一个httpServer用于握手。当客户端建立连接时,首先会进行握手,会触发server的upgrade事件,从而进入到服务端的处理函数中去。在这个函数中,会创建一个wsRequest对象,并将它作为request事件的参数发送出去。接下来根据客户端的Accept情况来进入到handleRequestAccepted,接下来connect事件会被触发。

var upgradeHandler = this._handlers.upgrade;
this.config.httpServer.forEach(function(httpServer) {
    httpServer.on(‘upgrade‘, upgradeHandler);
});

handleUpgrade:
var wsRequest = new WebSocketRequest(socket, request, this.config);
try {
    wsRequest.readHandshake();
}
wsRequest.once(‘requestAccepted‘, this._handlers.requestAccepted);
    wsRequest.once(‘requestResolved‘, this._handlers.requestResolved);
if (!this.config.autoAcceptConnections && utils.eventEmitterListenerCount(this, ‘request‘) > 0) {
    this.emit(‘request‘, wsRequest);
}

handleRequestAccepted:
this.connections.push(connection);
this.emit(‘connect‘, connection);

另外,WebsocketServer继承了EventEmitter,所以具有事件处理的能力:

util.inherits(WebSocketServer, EventEmitter);
时间: 2024-12-18 14:11:48

每天看一片代码系列(二):WebSocket-Node的相关文章

每天看一片代码系列(一):stream.js

简介 stream.js是一个小型的js库,用于处理stream相关的操作.这里的stream是指一种数据结构,它像数组一样,可以放置多个类型的数据,但是并不限制长度,甚至可以达到无限长.可以对该数据结构进行检索.修改.追加等种种操作.由于其长度不限这一特性,使得它与通常意义下的数据结构有明显的区别. API stream提供的API包含三种. 第一种是创建类.包括: new Stream(head, functionReturingTail) 第二个参数是一个放回除第一个元素之外剩下的元素的方

每天看一片代码系列(三):codepen上一个音乐播放器的实现

今天我们看的是一个使用纯HTML+CSS+JS实现音乐播放器的例子,效果还是很赞的: codePen地址 HTML部分 首先我们要思考一下,一个播放器主要包含哪些元素.首先要有播放的进度信息,还有播放/暂停或者上一首下一首等必要的按钮,同时还要显示一些当前播放的音乐名称等信息.播放多首歌曲时,要显示播放列表...因此,从语义上可以构造出基本的HTML结构: // 背景区块,用于显示当前播放音乐的图片 <div class='background' id='background'></di

每天看一片代码系列(四):layzr.js,处理图片懒加载的库

所谓图片的懒加载,即只有当图片处于或者接近于当前视窗时才开始加载图片.该库的使用方法非常简单: var layzr = new Layzr({ attr: 'data-layzr', // attr和retinaAttr必须至少有一个,用于指定对应的图片 retinaAttr: 'data-layzr-retina', // 一般对应的图像比attr要高清 threshold: 0, // 距离视窗的距离为多少时开始加载 callback: null // 回调函数 }); 代码解析 首先是包装

[Perl系列二-实战] 1. Perl 读取代码的行数

前言 有的时候有这些需求: 1. 统计一个文件的行数 2. 统计一个源代码的有效行数.排除空行和注释行 3. 统一一个目录下各种文件的数量和行数 要达成这些需求,很多语言多可以做到, 但是使用Perl 应该是一个不错的选择 读取文件行数 读取一个文件的总行数(空行和注释都包含) 使用Perl 很简单 open(FILE ,<>); my $lines_counter = 0; while(<>) { $lines_counter += 1; } print "lines:

swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?

date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowft 启动阶段的那些事儿 小伙伴刚接触 swoft 的时候会感觉 压力有点大, 更直观的说法是 难. 开发组是不赞成 难 这个说法的, swoft 的代码都是 php 实现的, 而 php 又是 世界上最好的语言, swoft 的代码阅读起来是很轻松的. 之后开发组会用 系列源码 解读文章, 深入解析

highcharts 结合phantomjs纯后台生成图片系列二之php2

上篇文章中介绍了phantomjs的使用场景,方法. 本篇文章详细介绍使用php,highcharts 结合phantomjs纯后台生成图片.包含一步步详细的php代码 一.highcharts 结合phantomjs纯后台生成图片系列的准备: 下载phantomjs解析插件,从highcharts官方下载所需插件. 新建一个工程文件夹phantomjs,所必备的js文件有: highcharts 结合phantomjs纯后台生成图片系列二之php 其中jquery.js为 v1.7.1; hi

iOS开发UINavigation系列二——UINavigationItem

iOS开发UINavigation系列二--UINavigationItem 一.引言 UINavigationItem是导航栏上用于管理导航项的类,在上一篇博客中,我们知道导航栏是通过push与pop的堆栈操作来对item进行管理的,同样,每一个Item自身也有许多属性可供我们进行自定制.这篇博客,主要讨论UINavigationItem的使用方法. UINavigationBar:http://my.oschina.net/u/2340880/blog/527706. 二.来说说UINavi

原始套接字基础(原始套接字系列二)

在进入Raw Socket多种强大的应用之前,我们先讲解怎样建立一个Raw Socket及怎样用建立的Raw Socket发送和接收IP包. 建立Raw Socket 在Windows平台上,为了使用Raw Socket,需先初始化WINSOCK: // 启动 WinsockWSAData wsaData;if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0){ cerr << "Failed to find Winsock 2.1 or

WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProperty)只能拥有一个binding. 这一点可以通过设置binding对象的方法名得知: public static BindingExpressionBase SetBinding( DependencyObject target, DependencyProperty dp, BindingB