动用Node.js+Socket.IO搭建WebSocket实时应用

Flappy Bird(Swift
版)  

WebSocket实战

本文将为差不多人口在线聊应用作为实例场景,我们先来确定此聊天应用的主干要求。

Swift 语言展示点 (Matt
Galloway)
 

需求分析

1、兼容不支持WebSocket的亚版本浏览器。
2、允许客户端有平等之用户称。
3、进入聊天室后可以看眼前在线的用户和在线人数。
4、用户上线或剥离,所有在线的客户端应该实时更新。
5、用户发送信息,所有客户端实时吸收。

于实际上的支付过程被,为了使WebSocket接口构建Web应用,我们第一得构建一个落实了
WebSocket规范的服务端,服务端的兑现非给平台及出语言的界定,只待遵循WebSocket规范即可,目前已经冒出了有些比较成熟的WebSocket服务端实现,比如本文使用的Node.js+Socket.IO。为什么选用这方案吗?先来简单介绍下他们少。

Swift 介绍

客户端代码实现

进入客户端工作目录/workspace/wwwroot/plhwin/demo.plhwin.com/chat,新建一个index.html:

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″>
<meta name=”format-detection” content=”telephone=no”/>
<meta name=”format-detection” content=”email=no”/>
<meta content=”width=device-width, initial-scale=1.0,
maximum-scale=1.0, minimum-scale=1.0, user-scalable=0″
name=”viewport”>
<title>多人聊天室</title>
<link rel="stylesheet" type="text/css" href="./style.css" />

<script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script>
</head>
<body>

请先输入你在聊天室的昵称


<script type="text/javascript" src="./client.js"></script>

</html>

方的html内容己并未呀好说的,我们第一看里面的4单公文要:

1、realtime.plhwin.com:3000/socket.io/socket.io.js
2、style.css
3、json3.min.js
4、client.js

第1独JS是Socket.IO提供的客户端JS文件,在前方安装服务端的步调中,当npm安装了socket.io并加建筑起WebServer后,这个JS文件就可以健康访问了。

第2个style.css文件没什么好说的,就是体制文件而已。

第3个JS只以IE8以下版本的IE浏览器中加载,目的是让这些没有版本的IE浏览器也能够处理json,这是一个开源之JS,详见:http://bestiejs.github.io/json3/

第4个client.js举凡共同体的客户端的事体逻辑实现代码,它的情节如下:

<pre>
(function () {
var d = document,
w = window,
p = parseInt,
dd = d.documentElement,
db = d.body,
dc = d.compatMode == ‘CSS1Compat’,
dx = dc ? dd: db,
ec = encodeURIComponent;

w.CHAT = {
    msgObj:d.getElementById("message"),
    screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight,
    username:null,
    userid:null,
    socket:null,
    //让浏览器滚动条保持在最低部
    scrollToBottom:function(){
        w.scrollTo(0, this.msgObj.clientHeight);
    },
    //退出,本例只是一个简单的刷新
    logout:function(){
        //this.socket.disconnect();
        location.reload();
    },
    //提交聊天消息内容
    submit:function(){
        var content = d.getElementById("content").value;
        if(content != ''){
            var obj = {
                userid: this.userid,
                username: this.username,
                content: content
            };
            this.socket.emit('message', obj);
            d.getElementById("content").value = '';
        }
        return false;
    },
    genUid:function(){
        return new Date().getTime()+""+Math.floor(Math.random()*899+100);
    },
    //更新系统消息,本例中在用户加入、退出的时候调用
    updateSysMsg:function(o, action){
        //当前在线用户列表
        var onlineUsers = o.onlineUsers;
        //当前在线人数
        var onlineCount = o.onlineCount;
        //新加入用户的信息
        var user = o.user;

        //更新在线人数
        var userhtml = '';
        var separator = '';
        for(key in onlineUsers) {
            if(onlineUsers.hasOwnProperty(key)){
                userhtml += separator+onlineUsers[key];
                separator = '、';
            }
        }
        d.getElementById("onlinecount").innerHTML = '当前共有 '+onlineCount+' 人在线,在线列表:'+userhtml;

        //添加系统消息
        var html = '';
        html += '<div class="msg-system">';
        html += user.username;
        html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
        html += '</div>';
        var section = d.createElement('section');
        section.className = 'system J-mjrlinkWrap J-cutMsg';
        section.innerHTML = html;
        this.msgObj.appendChild(section);   
        this.scrollToBottom();
    },
    //第一个界面用户提交用户名
    usernameSubmit:function(){
        var username = d.getElementById("username").value;
        if(username != ""){
            d.getElementById("username").value = '';
            d.getElementById("loginbox").style.display = 'none';
            d.getElementById("chatbox").style.display = 'block';
            this.init(username);
        }
        return false;
    },
    init:function(username){
        //客户端根据时间和随机数生成uid,这样使得聊天室用户名称可以重复。实际项目中,如果是需要用户登录,那么直接采用用户的uid来做标识就可以
        this.userid = this.genUid();
        this.username = username;

        d.getElementById("showusername").innerHTML = this.username;
        this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
        this.scrollToBottom();

        //连接websocket后端服务器
        this.socket = io.connect('ws://realtime.plhwin.com:3000');

        //告诉服务器端有用户登录
        this.socket.emit('login', {userid:this.userid, username:this.username});

        //监听新用户登录
        this.socket.on('login', function(o){
            CHAT.updateSysMsg(o, 'login');  
        });

        //监听用户退出
        this.socket.on('logout', function(o){
            CHAT.updateSysMsg(o, 'logout');
        });

        //监听消息发送
        this.socket.on('message', function(obj){
            var isme = (obj.userid == CHAT.userid) ? true : false;
            var contentDiv = '<div>'+obj.content+'</div>';
            var usernameDiv = ''+obj.username+'';

            var section = d.createElement('section');
            if(isme){
                section.className = 'user';
                section.innerHTML = contentDiv + usernameDiv;
            } else {
                section.className = 'service';
                section.innerHTML = usernameDiv + contentDiv;
            }
            CHAT.msgObj.appendChild(section);
            CHAT.scrollToBottom();  
        });

    }
};
//通过“回车”提交用户名
d.getElementById("username").onkeydown = function(e) {
    e = e || event;
    if (e.keyCode === 13) {
        CHAT.usernameSubmit();
    }
};
//通过“回车”提交信息
d.getElementById("content").onkeydown = function(e) {
    e = e || event;
    if (e.keyCode === 13) {
        CHAT.submit();
    }
};

})();
</pre>

至今所有的编码开发工作周成功了,在浏览器中开拓http://demo.plhwin.com/chat/就可以看到效能了,后续我会把演示代码提交到Github上。

本例只是一个概括的Demo,留下2个关于项目扩大的思维:

1、假设是一个在线客服系统,里面来好多底铺采用你的劳动,每个店自己的用户可经过一个直属URL地址上该店铺的聊天室,聊天是一定之,每个企业可新建多只客服人员,每个客服人员好又与客户端的基本上个用户聊天。

2、又比方是一个当线WebIM系统,实现类似微信,qq的功效,客户端好见到好友在线状态,在线列表,添加好友,删除好友,新建群组等,消息之殡葬除了支持中心的亲笔外,还能够支持表情、图片与文书。

有趣味的同校可以继承深入研讨。

[译] Rust 创始人 Graydon Hoare 对 Swift 的看法
(@CSDN)
 

编码实现

事先上演示效果图:

图片 1

可以点击这里查看在线演示。整个开发过程非常简单,下面简单记录了付出步骤:

LearnSwift.tips  

WebSocket简介

言到Web实时推送,就不得不说WebSocket。在WebSocket出现之前,很多网站为了促成实时推送技术,通常使用的方案是轮询(Polling)和Comet技术,Comet又只是划分为简单栽实现方式,一种植是增长轮询机制,一种叫做流技术,这片栽方式实在是本着轮询技术的改进,这些方案带来大肯定的败笔,需要由浏览器对服务器发HTTP
request,大量消耗服务器带富和资源。面对这种气象,HTML5定义了WebSocket协议,能还好的节约服务器资源和牵动富并贯彻真正意义上的实时推送。

WebSocket商量本质上是一个冲TCP的商议,它由通信协议和编程API组成,WebSocket能够当浏览器和服务器之间建立双向连接,以基于事件的点子,赋予浏览器实时通信能力。既然是双向通信,就代表服务器端和客户端可而且发送并应请求,而不再像HTTP的要和响应。

为建立一个WebSocket连接,客户端浏览器首先使奔服务器发起一个HTTP请求,这个请与普通的HTTP请求例外,包含了一部分附加头信息,其中附加头信息”Upgrade:
WebSocket”表明这是一个提请协议升级的HTTP请求,服务器端解析这些附加的峰信息然后有对信息返回给客户端,客户端和劳动器端的WebSocket连接就成立起来了,双方便可以通过是连续通道自由的传递信息,并且是连续会连存在直到客户端或者服务器端的某平正主动的倒闭连接。

一个突出WebSocket客户端请求头:

图片 2

面前说到WebSocket是HTML5遭新增的如出一辙栽通信协议,这象征有始终版浏览器(主要是IE10以下版本)并无负有这个职能,
经过百度统计的明数量显示,IE8目前以以33%的市场份额占据榜首,好当chrome浏览器市场份额逐年升高,现在盖过26%的市场份额位居第二,同时微软新近宣布已针对IE6的技术支持并提醒用户更新至新本子浏览器,这个早已给很多前端工程师也底峰疼的浏览器有望退出历史舞台,再长几乎所有的智能手机浏览器都支持HTML5,所以让WebSocket的实战意义很益,但是无论如何,我们实际的型被,仍然使考虑没有版本浏览器的匹配方案:在支撑WebSocket的浏览器被使新技巧,而以未支持WebSocket的浏览器里启用Comet来接收发送信息。

Swift 教程

服务端代码实现

眼前说到的index.js运转在服务端,之前的代码只是一个简练的WebServer欢迎内容,让我们将WebSocket服务端完整的贯彻代码加入进去,整个服务端就足以拍卖客户端的伸手了。完整的index.js代码如下:

<pre>
var app = require(‘express’)();
var http = require(‘http’).Server(app);
var io = require(‘socket.io’)(http);

app.get(‘/’, function(req, res){
res.send(‘<h1>Welcome Realtime Server</h1>’);
});

//在线用户
var onlineUsers = {};
//当前在线人数
var onlineCount = 0;

io.on(‘connection’, function(socket){
console.log(‘a user connected’);

//监听新用户加入
socket.on('login', function(obj){
    //将新加入用户的唯一标识当作socket的名称,后面退出的时候会用到
    socket.name = obj.userid;

    //检查在线列表,如果不在里面就加入
    if(!onlineUsers.hasOwnProperty(obj.userid)) {
        onlineUsers[obj.userid] = obj.username;
        //在线人数+1
        onlineCount++;
    }

    //向所有客户端广播用户加入
    io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
    console.log(obj.username+'加入了聊天室');
});

//监听用户退出
socket.on('disconnect', function(){
    //将退出的用户从在线列表中删除
    if(onlineUsers.hasOwnProperty(socket.name)) {
        //退出用户的信息
        var obj = {userid:socket.name, username:onlineUsers[socket.name]};

        //删除
        delete onlineUsers[socket.name];
        //在线人数-1
        onlineCount--;

        //向所有客户端广播用户退出
        io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
        console.log(obj.username+'退出了聊天室');
    }
});

//监听用户发布聊天内容
socket.on('message', function(obj){
    //向所有客户端广播发布的消息
    io.emit('message', obj);
    console.log(obj.username+'说:'+obj.content);
});

});

http.listen(3000, function(){
console.log(‘listening on *:3000’);
});
</pre>

《Swift
特刊》地址:http://swift.manong.io/

Web领域的实时推送技术,也受称作Realtime技术。这种技术如果上的目的是叫用户不欲刷新浏览器就好拿走实时更新。它富有广阔的应用场景,比如在线聊天室、在线客服系统、评论系统、WebIM等。

Swift 资料

Node.js

Node.js采用C++语言编写而成为,它不是Javascript应用,而是一个Javascript的运作环境,据Node.js创始人Ryan
Dahl回忆,他最初梦想采取Ruby来描写Node.js,但是后来发觉Ruby虚拟机的习性不可知满足他的要求,后来客尝试使用V8引擎,所以选择了C++语言。

Node.js支持的体系包括*nux、Windows,这代表程序员可以编制系统级或者服务器端的Javascript代码,交给Node.js来诠释实施。Node.js的Web开发框架Express,可以扶持程序员快速建立web站点,从2009年生至今日,Node.js的成材之速度显著,其发展前景获得了技术社区的充分肯定。

So So
Swift
 

安装Node.js

因自己的操作系统,去Node.js官网下载安装即可。如果成功安装。在指令执行输入node -vnpm -v应会见到相应的版本号。

<pre>
node -v
v0.10.26
npm -v
1.4.6
</pre>

Let’s
Swift
 

Socket.IO

Socket.IO是一个开源的WebSocket库,它经过Node.js实现WebSocket服务端,同时为提供客户端JS库。Socket.IO支持因事件吧底蕴的实时双向通讯,它可干活以任何平台、浏览器还是移动装备。

Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自行根据浏览器选择符合之报导方式,从而被开发者可以聚焦到效果的兑现而无是阳台的兼容性,同时Socket.IO具有无可争辩的长治久安和性质。

Swift
介绍  

搭建WebSocket服务端

斯环节我们尽量的设想实际生产条件,把WebSocket后端服务增加建成一个丝上得以用域名访问的劳务,如果您是于地头开发环境,可以转移本地ip地址,或者采用一个虚拟域名指向本地ip。

先上到公的办事目录,比如
/workspace/wwwroot/plhwin/realtime.plhwin.com,新建一个叫做也
package.json的文本,内容如下:
<pre>
{
“name”: “realtime-server”,
“version”: “0.0.1”,
“description”: “my first realtime server”,
“dependencies”: {}
}
</pre>

接下使用npm一声令下安装expresssocket.io
<pre>
npm install –save express
npm install –save socket.io
</pre>
设置成功后,应该可以看到工作目录下生成了一个号称吧node_modules的文书夹,里面分别是expresssocket.io,接下好起编制服务端的代码了,新建一个文本:index.js

<pre>
var app = require(‘express’)();
var http = require(‘http’).Server(app);
var io = require(‘socket.io’)(http);

app.get(‘/’, function(req, res){
res.send(‘<h1>Welcome Realtime Server</h1>’);
});

http.listen(3000, function(){
console.log(‘listening on *:3000’);
});
</pre>

一声令下执行运行node index.js,如果一切顺利,你应当会视返回的listening on *:3000字样,这证明服务既成搭建了。此时浏览器被开辟http://localhost:3000有道是好视正常的迎页面。

只要您想使给服务运作在线上服务器,并且可以透过域名访问的言语,可以下Nginx做代办,再nginx.conf中添加如下配置,然后以域名(比如:realtime.plhwin.com)解析及服务器IP即可。
<pre>
server
{
listen 80;
server_name realtime.plhwin.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
</pre>

形成以上步骤,http://realtime.plhwin.com:3000的后端服务就是如常搭建了。

图片 3

Swift
绘图程序(@林泰前)
 

Swift
中文网
 

Apple Swift
编程语言入门教程(@明哥选C)
 

多重教程:使用 Swift 开发 iOS 应用 (Jameson
Quave)
 

GitHub 上的开源 Swift
项目
 

Swift 网站

《Swift
特刊》地址:http://swift.manong.io/

Apple 官方教程《The Swift Programming
Language》  

Swift
开发视频教程【入门篇】(@51CTO学院)
 

专栏内容将保持更新,欢迎投稿。每当线投稿地址

Apple Swift
语言基础教程(@极客学院_jikexueyuan)  

Swift 观点

Swift
China(@老甘)
 

Swift 新手入门汇集帖
(@CocoaChina)
 

雨燕中文(@罗罗磊磊)
 

Swift 视频

Swift 系列视频教程 (Skip
Wilson)
 

Swift 编程语言资料大合集
(@CSDN_CODE)  

[译] Swift 首席架构师 Chris Lattner
简介(黄利民)
 

Swift 项目

[PDF] Swift vs Scala
2.11
 

Swift 简介
(@peng_gong)
 

Swift
学习资源(刘兰涛)
 

专栏内容将保障更新,欢迎投稿。每当线投稿地址

从 0 开始学
Swift(@传课网)
 

什么评论 Swift
语言?(@知乎)  

Learn
Swift
 

2048(Swift
版)
 

Swift 速查表 (Ray
Wenderlich)
 

至于 Swift 的一些初始意见
(@onevcat)