WebSocket学习(一)——基于socket.io实现简单多人聊天室

前言

什么是Websocket呢?
咱们都知道在Http协议中,客户端与服务器端的通讯是靠客户端发起请求,而后服务器端收到请求再进行回应,这个过程当中,客户端是主动的,服务器端是被动的。Websocket协议就不同了,它是基于TCP的一种新的网络协议,它与Http协议不一样之处就在于Websocket能实现服务器端主动推送消息到客户端,服务器端与客户端都能发起通讯,这一次,服务器端终于也拥有了主动权。javascript

什么是socket.io
socket.io封装了Websocket以及其余的一些协议,而且实现了Websocket的服务端代码。同时还有很强的兼容性,兼容各类浏览器以及移动设备。有了它,咱们能更方便快捷地实现服务器端与客户端之间的实时通信。css

实现功能简述

要实现多人聊天室的核心就是区分当前用户发送的消息与其余用户发送的消息,在这里我经过用户登陆使用的用户名来进行区分。因此用户进入首先展现登陆页面。html

图1.登陆页面
登陆成功以后,新用户加入聊天室
图2.登陆成功
若是用户重名,会弹出提示,保持吴彦祖的登陆状态,咱们再打开一个标签,输入“吴彦祖”查看效果
图3.昵称重复
只有当昵称惟一时,才容许登陆,咱们再登陆一个查看效果
图4.登陆提示
能够看到,当新用户登陆时,其余在线用户会收到提示,接下来就是发送消息了
图5.发送消息展现
发送的消息是实时推送的,当前用户发送的消息与其余用户发送的消息对话框作了区分。
当用户退出时,系统也会给出提示,效果以下
图6.退出登陆
怎么样,有没有兴趣继续了解呢?下面就开始着手开发吧。前端

环境搭建

1.安装node.js

后端服务是用的node.js,因此咱们首先要进行安装,安装方法很简单,我在以前一篇文章也提过。首先在node.js官网下载稳定版本,下载完成后点击安装,安装过程也很简单,一直next便可,安装完成会自动添加nodenpm环境变量。java

检验是否安装成功,在cmd输入命令 node -v,回车 及 npm -v,回车,如出现下图所示版本信息,表示安装成功node

2.新建项目文件夹,安装socket.io

新建文件夹chatroom,在这里我把它建到D盘根目录下。打开cmd,定位到刚建的chatroom文件夹下,输入npm install socket.io安装socket.iojquery

安装socket.io
安装完成以后,能够看到文件夹下多了node_modules文件,里面全是刚下载的socket.io依赖包。git

3.新建页面

chatroom文件夹下新建页面文件index.html,样式chat.css,后端jsapp.js,前端jschat.js,并下载jquery.min.js,socket.io.js。再下载一张图片做为用户头像,放在images/user/下。
目录结构以下
文件结构github

好了,环境搭建完成,开始撸码吧。web

项目搭建

1.构建node服务器

在app.js里面构建服务器

/*app.js*/
/*构建http服务*/
var app = require('http').createServer()
/*引入socket.io*/
var io = require('socket.io')(app);
/*定义监听端口,能够自定义,端口不要被占用*/
var PORT = 8081;
/*监听端口*/
app.listen(PORT);

console.log('app listen at'+PORT);

接着启动服务
打开cmd,定位到app.js所在目录,输入node app.js,如图所示,打印出了咱们写的内容,表示服务启动成功。
启动服务

2.创建服务端socket链接监听

先给你们简单讲一下服务器端与客户端通讯的基本方法
你们能够看一下socket.io的文档
(1)socket.emit
客户端与服务器端之间发送消息是用emit
例如客户端向服务端发送登陆请求
socket.emit('login',{username:uname}) login是自定义的事件,后面是带的参数
(2)socket.on
服务器端要接收客户端发送的login事件,就得对该事件进行监听
socket.on('login',function(data){})在回调函数中进行处理
同理,服务器端也能够向客户端发送事件,只要客户端也对该事件进行监听就行
(3)io.sockets.emit
服务器端向链接的全部客户端发送消息得用io.sockets.emit
(4)socket.broadcast.emit
给除了本身之外的客户端广播消息

/*app.js*/
/*构建http服务*/
var app = require('http').createServer()
/*引入socket.io*/
var io = require('socket.io')(app);
/*定义监听端口,能够自定义,端口不要被占用*/
var PORT = 8081;
/*监听端口*/
app.listen(PORT);

/**
*监听客户端链接
*io是咱们定义的服务端的socket
*回调函数里面的socket是本次链接的客户端socket
*io与socket是一对多的关系
*/
io.on('connection', function (socket) {
  /*全部的监听on,与发送emit都得写在链接里面,包括断开链接*/
})
console.log('app listen at'+PORT);

3.前端页面

index.html页面中需引入socket.io.jssocket.io.js下载地址

/*index.html*/
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <title>聊天室</title>
    <link type="text/css" rel="stylesheet" href="css/chat.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/socket.io.js"></script>
    <script type="text/javascript" src="js/chat.js"></script>
</head>
<body>
    /*登陆界面*/
    <div class="login-wrap">
        <div class="login-con">
            <h3>用户登陆</h3>
            <input type="text" placeholder="请输入昵称" id="loginName">
            <button class="login-btn">登陆</button>
        </div>
    </div>
    
    /*聊天界面,一开始隐藏,用户登陆成功后再显示*/
    <div class="chat-wrap hide">
        <h1>多人聊天室</h1>
        <div class="chat-con clearfix"></div>
        <div class="bottom">
            <input type="text" id="sendtxt">
            <button class="sendBtn">发送</button>
        </div>
    </div>
</body>
</html>

4.样式

样式能够本身编写,我这里随便写了一下

/*公共样式*/
*{padding:0; margin:0;}
html,body{width:100%;height: 100%;}
.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}
.clearfix{*zoom:1}
.cred{color:#f03e3e;}
.cgreen{color:#459d36;}
.hide{display:none;}
.fr{float:right;}
.fl{float: left;}
.rela{position: relative;}
.abs{position:absolute;}
h1{position: fixed; z-index:20; width: 100%; height:50px; line-height:50px; font-size:20px; left: 0; top: 0; background: #000; color: #fff;}

/*登陆界面*/
.login-wrap{background:#e7e7e7;width:100%;height:100%; text-align:center;}
.login-con{padding-top: 50px;}
.login-con h3{margin-bottom: 20px;}
.login-con input{width:60%; display:block; margin:0 auto; height: 40px; line-height: 40px; margin-bottom: 20px;}
.login-con button{width:60%;display:block; margin:0 auto; height: 40px; line-height:40px; border:none; background:#459d36; color:#fff; border-radius:5px;}

/*聊天界面*/
.chat-wrap{width: 100%; height: 100%;overflow-y:scroll; background:#e7e7e7; text-align:center;}
.chat-con{padding: 50px 0; background:#e7e7e7;}
.chat-con p{display:inline-block; padding:5px 10px; background:#999;border-radius:5px; color:#fff; margin:5px 0;}
.bottom{position:fixed;bottom:0; left: 0; width:100%; height: 50px; background: #fff;}
.bottom input{width: 78%; height: 50px; line-height: 50px; float:left;border:none;}
.bottom button{width: 20%;height: 50px; float: right; border:none; background:#459d36;color: #fff;}
.chat-item{width:100%; margin-bottom:20px;}
.item-right .message{background: #62b900;}
.item-left .message{background: #fff; margin-top:20px;}
.item-left .img{margin-right:10px;}
.item-left .uname{font-size:12px; left:50px; top:0;}
.chat-item .message{width:60%;display:block; padding:10px;border-radius:5px; margin-right:10px;}
.chat-item .img{display:inline-block; width:40px; height:40px; background:url(../images/user/user.jpg) no-repeat; background-size:100% 100%;}

5.逻辑编写

(1)登陆

客户端
浏览器端将得到的用户输入的昵称信息,发送到服务器端,告诉服务器端我要触发login事件。
在客户端chat.js中发送登陆事件

/*chat.js*/
$(function(){
    /*创建socket链接,使用websocket协议,端口号是服务器端监听端口号*/
    var socket = io('ws://localhost:8081');
    /*定义用户名*/
    var uname = null;

    /*登陆*/
    $('.login-btn').click(function(){
        uname = $.trim($('#loginName').val());
        if(uname){
            /*向服务端发送登陆事件*/
            socket.emit('login',{username:uname})
        }else{
            alert('请输入昵称')
        }
    })

})

服务器端
服务器端监听login事件,在后台打印出获取到的昵称信息。
在服务器端app.js中监听登陆事件

/*app.js*/
var app = require('http').createServer()
var io = require('socket.io')(app);
var PORT = 8081;

app.listen(PORT);
io.on('connection', function (socket) {
    /*监听登陆*/
    socket.on('login',function(data){
        console.log(data)
    })
})

console.log('app listen at'+PORT);

注:更改了app.js,需再次启动服务才能看见效果

打开cmd,按Ctrl+C退出上次服务,再次输入node app.js启动服务,打开浏览器,查看效果

能够看到,点击登陆按钮时,服务器端打印出了接收到的用户名信息,没问题,继续往下写登陆成功事件。

(2)登陆成功与失败

因为没有使用到数据库,因此咱们就定义一个用户数组,用户每次登陆以后,咱们就判断该昵称是否已存在,若是已存在就弹出提示,转到登陆失败事件,若是该昵称不存在数组里面,就视为新用户,转到登陆成功事件,而且将该昵称存入数组。

服务器端

/*app.js*/
var app = require('http').createServer()
var io = require('socket.io')(app);
var PORT = 8081;
/*定义用户数组*/
var users = [];

app.listen(PORT);
io.on('connection', function (socket) {
    /*是不是新用户标识*/
    var isNewPerson = true; 
    /*当前登陆用户*/
    var username = null;
    /*监听登陆*/
    socket.on('login',function(data){
        for(var i=0;i<users.length;i++){
            if(users[i].username === data.username){
                  isNewPerson = false
                  break;
            }else{
                  isNewPerson = true
            }
        }
        if(isNewPerson){
            username = data.username
            users.push({
              username:data.username
            })
            /*登陆成功*/
            socket.emit('loginSuccess',data)
            /*向全部链接的客户端广播add事件*/
            io.sockets.emit('add',data)
        }else{
            /*登陆失败*/
            socket.emit('loginFail','')
        }  
    })
})

console.log('app listen at'+PORT);

客户端

/*chat.js*/
$(function(){
    /*创建socket链接,使用websocket协议,端口号是服务器端监听端口号*/
    var socket = io('ws://localhost:8081');
    /*定义用户名*/
    var uname = null;

    /*登陆*/
    $('.login-btn').click(function(){
        uname = $.trim($('#loginName').val());
        if(uname){
            /*向服务端发送登陆事件*/
            socket.emit('login',{username:uname})
        }else{
            alert('请输入昵称')
        }
    })

    /*登陆成功*/
    socket.on('loginSuccess',function(data){
        if(data.username === uname){
            checkin(data)
        }else{
            alert('用户名不匹配,请重试')
        }
    })

    /*登陆失败*/
    socket.on('loginFail',function(){
        alert('昵称重复')
    })

    /*新人加入提示*/
    socket.on('add',function(data){
        var html = '<p>系统消息:'+data.username+'已加入群聊</p>';
        $('.chat-con').append(html);
    })

    /*隐藏登陆界面 显示聊天界面*/
    function checkin(data){
        $('.login-wrap').hide('slow');
        $('.chat-wrap').show('slow');
    }
})

再次重启服务,打开浏览器查看效果,登陆成功效果如上面图2所示,登陆失败效果如上面图3所示。

(3)退出登陆

退出登陆,只需服务器端在用户数组里面删除退出的用户便可。
服务器端

/*app.js*/
/*退出登陆*/
/*写在io.on('connection', function (socket) {})里面*/
socket.on('disconnect',function(){
    /*向全部链接的客户端广播leave事件*/
    io.sockets.emit('leave',username)
    users.map(function(val,index){
        if(val.username === username){
              users.splice(index,1);
        }
    })
 })

客户端

/*chat.js*/
/*退出群聊提示*/
socket.on('leave',function(name){
    if(name != null){
        var html = '<p>FBI warning:'+name+'已退出群聊</p>';
        $('.chat-con').append(html);
    }
})
(4)发送消息

客户端

/*chat.js*/
/*发送消息*/
$('.sendBtn').click(function(){
    sendMessage()
});
$(document).keydown(function(event){
    if(event.keyCode == 13){
        sendMessage()
    }
})
function sendMessage(){
    var txt = $('#sendtxt').val();
    $('#sendtxt').val('');
    if(txt){
        socket.emit('sendMessage',{username:uname,message:txt});
    }
}

服务器端

/*app.js*/
socket.on('sendMessage',function(data){
    io.sockets.emit('receiveMessage',data)
})

客户端

/*chat.js*/
/*接收消息*/
socket.on('receiveMessage',function(data){
    showMessage(data)
})

/*显示消息*/
function showMessage(data){
    var html
    if(data.username === uname){
        html = '<div class="chat-item item-right clearfix"><span class="img fr"></span><span class="message fr">'+data.message+'</span></div>'
    }else{
        html='<div class="chat-item item-left clearfix rela"><span class="abs uname">'+data.username+'</span><span class="img fl"></span><span class="fl message">'+data.message+'</span></div>'
    }
    $('.chat-con').append(html);
}

到这里,一个简单的多人聊天室已基本实现了,先回顾一下准备工做
(1)下载node.js
(2)安装socket.io
npm install socket.io
(3)服务器端构建http服务,引入socket.io,并设置监听端口

var app = require('http').createServer()
var io = require('socket.io')(app);
var PORT = 8081;
app.listen(PORT);

(4)客户端进行socket链接,使用websocket协议
var socket = io('ws://localhost:8081');


再回顾一下整个逻辑流程:
(1)客户端获取用户输入昵称,发送给服务器端;
(2)服务器端接收昵称,判断是否新用户,是则发送登陆成功事件,不然发送登陆失败事件
(3)客户端收到服务器端发送的登陆成功或失败事件,进行相应处理
(4)浏览器端获取登陆用户输入的消息,将消息与用户昵称一块儿发送给服务器端
(5)服务器端接收到用户发送的消息,广播该消息给当前链接的全部客户端
(6)客户端接收服务器端发送来的消息,判断昵称是不是本身,进行相应对话框显示
最后附上github地址

相关文章
相关标签/搜索