node.js学习笔记(1)——express+socket.io模块构建简易聊天室

一、开发环境及参考书籍

IDE是vscode,已经配置好node环境等

参考书籍:《node.js入门经典》

二、安装模块

1.新建工程文件夹

2.新建package.json文件,代码如下

{
    "name": "socketio-express-example",
    "version": "0.0.1",
    "private": true,
    "scripts": {
        "start": "node app.js"
    },
    "dependencies": {
        "express": "^4.16.3",
        "socket.io": "^2.1.1"
    }
}

注:(1)scripts中可以写入各种命令,具体情况可以在网上搜索,在这里,写入 "start":"node app.js" 后可以在vscode中选择npm start方式运行,其实等同于在命令行中执行node app.js

(2)dependencies中包含了项目中使用到的各种模块,node是一个开源项目,这些模块都是各位大佬以及编程爱好者贡献的。格式为"名称":"版本号",如果不清楚版本号是什么的话可以直接到github上找源码,例如socket.io的readme文档中包括了基本信息,链接https://github.com/socketio/socket.io

(3)express模块用于搭建服务器,socket.io模块用于前后端传递消息

3.运行npm install安装所需模块,安装后可以在工程文件夹下看到node_modules文件夹,里面存放了涉及的所有模块

三、搭建服务器

1.新建app.js文件,写入如下代码,可以创建一个本地服务器,端口号是3000

var express = require('express');
var app = express();

var http = require('http');
var server = http.createServer(app);
var path = require('path');
  
server.listen(3000);

//路由,访问"127.0.0.1:3000/"时调用
app.use('/', function(req, res){
    res.sendFile(path.join(__dirname, '/index.html'));
});

2.在工程目录下新建index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Socket.IO Express Example</title>
</head>
<body>
    <h1>Socket.IO Express Example</h1>
</body>
</html>

3.使用npm start命令运行任务,可以看到服务器已经搭建完成

四、socket.io模块

1. 使用socket.io模块可以实现服务器和浏览器之间的通信。在使用前必须将其添加到服务器端的node.js和客户端的jQuery中。

在node.js中引入io模块

var io = require('socket.io').listen(server);

在index.html中引入socket.io

<script src="/socket.io/socket.io.js"></script>
<script>        
    var socket = io.connect();
</script>

注:index.html中的文件路径/socket.io/socket.io.js是由socket.io库自动提供的,只要是在同一服务器上运行服务端和客户端

2. 主要用法

(1)服务端中连接与断开连接的监听,所有的其他socket.io操作都应在connetion的回调函数中进行

io.sockets.on('connection', function(socket){
    //连接后的代码
    socket.on('disconnect', function(){
        //断开连接后
    });
});

(2)使用sockets.on('data', function(){ })监听数据,使用socket.emit('data', value)发送数据

前端的数据发送和接收

socket.emit('data', dataValue);
socket.on('message', function(message){ });

服务端数据的发送和接收

socket.emit('message', messageValue);
socket.on('data', function(data){ });

注:dataValue的值可以为各种数据类型,比如对象形式,String形式等

五、业务逻辑

效果图如下:

未注册时界面需要输入昵称,注册后界面包括消息框、在线人员昵称显示,对话内容显示。

1. 前端HTML

<form id="set-nickname">
        <label for="nickname">Nickname:</label>
        <input type="text" id="nickname">
        <input type="submit">
</form>
<form id="send-message">
        <textarea id="message"></textarea>
        <input type="submit">
</form>
<section id="nicknames">
        <ul></ul>
</section>
<section id="messages">

</section>

主要设置了2个表单,一个输入昵称,一个输入消息

2. 前端Javascript

var socket = io.connect();
jQuery(function($){
    var nickname = $('#nickname');
    var setNicknameForm = $('#set-nickname');
    var nicknameList = $('#nicknames ul');
    var messageForm = $('#send-message');
    var message = $('#message');
    var messages = $('#messages');
    //注册昵称
    setNicknameForm.submit(function(event){
        event.preventDefault();
        socket.emit('nickname', nickname.val(), function(data){
            if(data){
                console.log('Nickname set successfully');
                setNicknameForm.hide();
                messageForm.show();
            }
            else{
                setNicknameForm.prepend('<p>Sorry, that nickname is already taken.</p>');
            }
        });
    });
    //提交输入的消息
    messageForm.submit(function(event){
        event.preventDefault();
        socket.emit('user message', message.val());
            message.val('').focus();
        });
        socket.on('nicknames', function(data){
            var html = '';
            for(var i=0; i<data.length; i++){
                html += '<li>' + data[i] + '</li>';
            }
        nicknameList.empty().append(html);
    });
    //在消息列表更新消息
    socket.on('user message', function(data){
        messages.append('<p><strong>'+data.nickname+'</strong>'+data.message+'</p>');
    });
});

主要是对两个表单内容进行处理,使用jQuery获取页面元素然后使用socket.emit传递数据到服务端。此外通过socket.on实时监听其他用户传来的消息并显示到页面上

3. 服务端代码

//使用数组存放用户姓名
var nicknames = [];

io.sockets.on('connection', function(socket){
    socket.on('nickname', function(data, callback){
        if(nicknames.indexOf(data) != -1){
            callback(false);
        }
        else{
            callback(true);
            nicknames.push(data);
            socket.nickname = data;
            console.log('Nicknames are: ' + nicknames);
            io.sockets.emit('nicknames', nicknames);
        }
    });
    socket.on('user message', function(data){
        io.sockets.emit('user message', {
            nickname: socket.nickname,
            message: data
        });
    });
    socket.on('disconnect', function(){
        if(!socket.nickname)
            return;
        if(nicknames.indexOf(socket.nickname) > -1){
            nicknames.splice(nicknames.indexOf(socket.nickname), 1);
        }
        console.log('Nicknames are: ' + nicknames);
        io.sockets.emit('nicknames', nicknames);
    });
});

和前端代码基本是对应的,断开连接后从昵称列表中删掉该昵称。服务端就像是一个中介,因为客户端和客户端不能直接交换信息,必须传递到服务端后由服务端再把消息分配给所有客户端。

六、问题所在

1. 新加入的用户看不到之前的消息。

这是因为其实所有的消息都是由提交过后添加到服务端的网页上的,比如A发了个消息,那么服务端接收到A的消息后直接广播给所有在线的客户端,客户端将这条消息添加到页面上,消息根本就没有保存,因此C登录后获取不了。

2. 刷新后直接掉线

因为刷新后会重新访问 / 路由,重新调用index.html文件

猜你喜欢

转载自blog.csdn.net/qq_41057206/article/details/82428548