2018/2/2 19:00:44当前位置推荐好文程序员浏览文章

之前我们在 IM即时聊天室(一):WebSocket 和 IM即时聊天室(二): Socket.io + Node.js
两篇文章中介绍了搭建一个IM的所需的技术栈和通信原理。那在这篇文章里我们就来详细说一下具体的应用并提供完整源码。
个人Blog地址:IM即时聊天室(三):项目详解及源码分析


PS: 这是我网络编程课的一个小作业,因为博主比较懒所以这篇文章的大部分由我的课程报告组成(逃)


实现目标

应用实例是一个多人聊天室程序,用户可以通过指定服务器IP进入聊天室和聊天室内其他用户进行聊天,聊天室的功能主要有文本、表情和图片传输,并在Web界面中显示。

需求分析

  1. 应用采用客户/服务器模式,分为客户端程序和服务器端程序。

  2. 用户通过客户端可以选择头像,发送信息、表情、图片

  3. 服务端可以同时接收多个客户端信息,并根据需求进行转发、广播或者回传给客户端特定的消息。

  4. 客户端程序和服务器程序通过网络交换聊天的字符串内容(其中表情和头像为特殊格式代码,图片为转码后获得的URL格式)

话不多说,先看程序效果展示:

这里写图片描述
这里写图片描述

技术实现

  1. 通信协议选择
    Balabalabala... 详见下面两篇文章
    IM即时聊天室(一):WebSocket
    IM即时聊天室(二):Socket.io + Node.js

  2. 客户端
    UI界面:HTML+CSS+Bootstrap自适应

界面交互:原生Javascript+jQuery

消息的发送和接收:Socket.io

PS:由于客户端代码太太太太太多了,我只挑比较有特点的的讲一下

登录啥的就不讲了,大概思路就是开始时隐藏聊天界面,登录成功后隐藏登录界面显出聊天界面,源码里有完整的注释

先贴一下客户端代码大致结构图:


这里写图片描述

比较有意思的代码是我仿照微信做了一个回到最新消息处的功能。

当我们在聊天时,我们会希望最新的消息实时显示出来,但是当我们在看历史消息时,可不希望被强行定位到最新消息处。并且在查看历史消息时,可以通过按钮回到最新消息处。

于是我们就要判断用户在查看历史消息还是正在聊天,方法就是判断最新的消息是否出现在了用户窗口里。

关键代码:

```/将页面下拉到最新消息处/    function scrollToEnd(){            var div = document.getElementsByTagName("div");                div_length = div.length-4;                    div[div_length].scrollIntoView({behavior: "smooth"});                  //平滑滚动,提高了用户体验        }        /判断当有新信息来时,用户是否在页面底端/    function isNewInWindow(){            var div = document.getElementsByTagName("div");            div_length = div.length-5;                if(isInWindow(div[div_length])){                return true;            }            return false;    }        /判定元素是否在界面内/    function isInWindow(x){                 if(x.getBoundingClientRect().top > window.innerHeight){                console.log("down");                return false;            }            else if(x.getBoundingClientRect().bottom < 0){                console.log("up");                return false;            }            return true;    }```
  1. 服务端
    服务端主要使用了Node.js+Socket.io来构建服务端,需要Node环境来运行。

主要的思路:请求HTTP服务 → 在此基础上使用WebSocket → 引入Socket.io模块并创建实例 → 用该实例进行socket监听 → 通过socket进行消息的发送、转发、群发等。

服务端关键代码:

```var app = require(http).createServer();var io = require(socket.io)(app);var PORT = 8081;/定义用户数组/var users = [];app.listen(PORT);io.on(connection,function (socket) { //监听    //发送、转发、群发消息})```
  1. 图片文件传输

图片传输部分我本来是想用Base64编码,先在客户端读取图片二进制信息并编成Base64格式发送给服务端,服务端转发后别的客户端接收后解码。

但是后面放弃了这一方式,因为直接传输二进制信息太慢了,于是改用了HTML5的Filereader方法。

这个方法有个优点就是其中的readAsDataURL函数可以直接将图片二进制信息转成URL格式,这样子我只用发送并转发一个简短的URL,客户端直接根据URL加载图片,省去了大量传输和解码时间。

关键代码:
```
//图片发送
document.getElementById(sendImage).addEventListener(change,
function() {
//检查是否有文件被选中
if (this.files.length != 0) {
//获取文件并用FileReader进行读取
var file = this.files[0],
reader = new FileReader();
if (!reader) {
return;
};
reader.onload = function(e) {
//读取成功,发送到服务器
socket.emit(sendImg,{
username:uname,
image: e.target.result,
date:new Date().toTimeString().substr(0, 8),headnum:headnum
});
};
reader.readAsDataURL(file);
};
}, false);

```
  1. 头像选择/表情包传输
    头像和表情已经预先放置在客户端中,用户在选择以后将产生特定格式的代码,放置到要发送的信息当中。

别的客户端接收到的消息时候,先用正则表达式检索信息中是否存在该格式代码,若有则加载相应的头像/表情,以达到效果。

关键代码,以表情为例:

//emojidocument.getElementById(emojiWrapper).addEventListener(click, function(e) {//获取被点击的对象var target = e.target;console.log(target);if (target.nodeName.toLowerCase() == img) {   //如果是表情图像则发送var sendtxt = document.getElementById(sendtxt);sendtxt.focus();sendtxt.value = sendtxt.value + [emoji: + target.num + ];};}, false);

源码

Gitbub地址: KMKNKK/Chatroom-WebSocket/tree/homework
注意是homework的branch
如果有疑问欢迎来问我~
QQ:895025751 Wechat:kk-dm506

重要文件结构:

│  ├─index.html  //客户端│      ├─css│      bg.jpg  //背景图│      chat.css                │      ├─images  //图片资源│  │  logo.png│  │  toNewMessage.png│  │  │  ├─emoji  //表情│  │      │  └─user   //头像│          │      ├─js│  │  app.js            //服务端JS│  │  chat.js           //客户端JS│  │  jquery.min.js     //依赖│  │  socket.io.js      //依赖│  │  │  └─.vscode│          settings.json│          ├─new                   //BootStrap和jQuery文件│  │  jquery-3.2.1.min.js     │  │  │  └─bootstrap-3.3.7-dist│      │              └─node_modules          //依赖文件
网友评论