当前位置: 首页 > 后端技术 > Node.js

教你玩网络聊天室

时间:2023-04-03 16:06:18 Node.js

一些废话:)最近在学校忙得不可开交,终于有了这么一段可以自由支配的时间,心里还是很酸爽的。当然,也有罪孽深重的,比如连续一周不吃早餐。其实现在回想起来,真的不能怪我,因为最近的天气实在是太差了!寒冷的!向上!嗯,为了减少卧床不起的罪恶感,不如学(gǎo)一点(diǎn)东(shì)西(qing)。废话不多说,进入正题。进入题目非常难看的聊天室,后面临时加上“v1.0”。毕竟没有经过任何迭代,写完就直接发布了。当然还有很多地方可以再乱来,比如:[]支持发图片[]支持发表情[]显示在线用户列表我在写这个demo的时候,一直处于边学边写的状态。学习资料主要是基于刘瓦勇的Node.js+WebSocket创建了一个名为Hichat的即时聊天程序,主流的搜索引擎和我最喜欢的技术社区SegmentFault作为补充。源代码已经上传到我的github。克隆到本地后,在终端运行如下两条命令:npminstallnodeserver然后打开浏览器,访问localhost,无需联网即可看到这个demo。预览输入用户名完成登录,即可开始与在线人聊天。准备工作当然,Node.js是必不可少的。这里有两篇很棒的Node.js教程:Node.Node.js包入门教学宝汇Node.js只需几行代码就可以实现一个服务器varhttp=require('http');http.createServer(function(request,response){response.writeHead(200,{'Content-type':'text/plain'});response.write('嘿你,我叫kyrieliu~');response.end();}).listen(8080);在Terminal中执行这段代码,访问http://localhost/:8080,可以看到一行字:Heyyou,mynameiskyrieliu~这表示你的node服务已经搭建好了,axi,js写后台逻辑,用你的脚趾思考每个人都认为这是一件很酷的事情。另外使用了两个包模块:expresssocket.ioexpress是node.js中管理路由响应请求的模块,根据请求的URL返回对应的HTML页面。这里我们使用预先写好的静态页面返回给客户端,只需要使用express指定返回页面的路径即可。如果不使用这个包,我们需要把HTML代码和后台JavaScript代码一起写来响应请求,不方便。Socket.io封装了websocket,也包含了其他的连接方式,比如Ajax。原因是并非所有浏览器都支持websockets。通过socket.io包,你不需要关心里面使用的连接方式。您可以使用socket.io在任何浏览器中建立异步连接。UI界面,乍一看就是那么简单(chǒu)(lòu),但是“麻雀虽小五脏俱全”,该有的还是有的,DOM结构直接贴在这里。

可以清晰的看到“心”、“肝”、“脾”、“肺”。(“肾”在哪里?咦,你以为我的新手机是从哪里来的?)至于那些醒目的类名,是因为项目中使用了Bootstrap,算是偷懒了。前端逻辑UI做好了,再想想这个聊天室的交互是怎么实现的。“你之前不是说了,用websocket。“这句话没错,但我这里指的是交互。毕竟,如果你写一个程序,你必须对程序中的逻辑“挑剔”(我使用这个成语是否正确)才能与服务器建立连接并输入昵称完成登录发送消息和接收消息,仔细想想,好像有这么多,那就开始一个一个的打通,和服务器建立连接吧,这里要注意,因为是聊天系统,所以和服务器建立连接的方式和通常的不一样,这里使用的方法协议是HTTPWebSocket,从而实现持久连接,简单说明一下,这里的“持久”是相对于HTTP这种“非持久化”的协议而言的(你是说HTTP的老婆会很羡慕WebSocket夫人的)。通过阅读Overear在知乎上的回答,大致说一下这两种协议的区别。HTTPHTTP的循环大致是这样的,一个请求,一个响应,这个请求就结束了;HTTP1.1改进了,增加了一个keep-alive。效果是在这个HTTP连接中,可以发送多个请求,也可以接受多个响应,但本质上是request=response,也就是说request和response永远是一个对应的,没有request的时候,服务器无法主动响应。当WebSocket客户端和服务端完成协议升级(HTTP->WebSocket),持久连接就建立了。它有多耐用?此连接可以继续存在。客户端或服务器主动关闭连接。与HTTP最大的区别在于服务端可以主动向客户端推送消息。在这个项目中,我们使用socket.io包模块来实现WebSocket。Socket.io不仅实现了对WebSocket的封装,还将其与Ajax轮询等实时通信方式一起封装成一个通用的接口。这样做的原因是当服务器不支持WebSocket时,可以转换为其他实现方式。啧啧啧,可谓是享受丝般的滑爽。下一部分是实施。前端引入socket.io.js文件后应该做什么?谈话很便宜,向您展示CODE.varsocket=io.connect();是的,就这么简单。不信你去看官方文档。输入昵称完成登录。这里的“登录”并不是真正的登录。执行io.connect()后,连接已经建立,这里正在处理一些交互行为。监听前端的连接事件。该事件的触发条件为:成功与服务器建立连接。socket.on('connect',function(){//做点什么});回调里面是此时要完成的DOM操作,例如:改变提示文字(最初是“正在连接服务器...”)显示覆盖层的焦点文本框。当用户输入昵称并点击登录按钮时,当前套接字向服务器触发登录事件:socket.emit('login',nickname);携带一个参数,是用户输入的昵称。服务端检查昵称合法性,通过时触发:socket.on('loginSuccess',function(){//1.隐藏登录层//2.用户可以愉快的和别人聊天~});if如果用户输入的昵称无效,则会触发:socket.on('loginFailed',function(){//1.提示用户昵称有问题//2.等待用户重新输入});注意这里的事件名称,比如login、loginSuccess、loginFailed都是自定义的,只要和服务器一致就可以了。发送消息想象一下用户发送消息的操作,并将其分解:输入文本->单击发送。就是这两个,ok,这里需要在发送按钮上挂一个事件,告诉服务器,“server服务器,这里是socketXXX,我给你发消息了,注意查收,结束。”插座。发出('msgSend',味精);携带一个参数,即用户的输入。接受消息的逻辑分为三种情况。自己发的消息。其他人发送的消息。系统提示信息。不要惊慌,一个一个地看。自己发送的消息自己发送的消息直接显示在聊天消息面板。接收自己发送的消息,无需后台交互。你只需要告诉后台,我给你发了这条消息。当然你也可以仿微信对你发送的消息进行处理:发送的时候在聊天面板添加你的消息->旁边放个小菊花或者加载->后台交互->隐藏消息成功的菊花;如果失败,小菊花会变成红色感叹号,表示用户发送失败。对于别人发送的消息,现在需要在前端创建一个监听事件来响应服务器端的“新消息”。socket.on('newMsg',function(nickname,msg){//显示这条新消息});回调函数中有两个参数nickname和msg,分别是消息发送者的昵称和消息内容。它是怎么来的?别着急,后面会在服务端的逻辑中讲到。这里只需要知道前端收到新消息时,因为涉及到显示新消息,所以需要这两个参数。系统提示信息关于系统提示信息,主要有两种:提示新用户加入和退出,显示当前在线用户数,大致是这样的,所以需要在前端socket上监听系统事件。('system',function(nickname,count,type){//1.根据系统事件类型提示用户(新加入或离开)//2.修改在线用户数});这里的三个参数也是必不可少的,nickname代表触发系统事件的用户的昵称,count代表当前在线的用户数,type代表事件类型(加入/离开)。同样,这三个参数也是从服务器传来的。后台逻辑对应前端,后台逻辑主要分为以下几个部分:服务建立、连接、用户登录、接受用户发送的消息和系统消息的广播、服务的处理varexpress=require('表达');varapp=express();varserver=require('http').createServer(app);vario=require('socket.io')(server);app.use('/',express.static(__dirname)+"/www"));server.listen(8080);因为我把前端文件(html/js/css)放在www文件夹下,所以用express指定返回给浏览器的页面路径.现在是这样的。当然,除了express,还必须引入socket.io模块并绑定到服务端。连接服务准备好了,如何建立连接呢?io.on('connection',function(socket){//做某事});像这样...?昂。你没看错,我也没有写错,这里对应前端逻辑:varsocket=io.connect();socket.on('connect',function(){//dosomething});连接建立后,所有关于socket活动的逻辑就可以开始写了。(仅供参考:当然是写在这个连接事件的回调中)用户登录还记得前端触发的登录事件叫什么吗?socket.emit('登录',昵称);叫做login,它还带了一个参数——user我想给自己起的昵称是nickname。好了,我们来写对应的后台逻辑socket.on('login',function(nickname){//dosomething});dosomethinghere是做什么的?也就是检查用户输入的昵称是否合法,比如是否已经存在,长度限制,符号限制等。球是麻袋,好像哪里不对……长度限制和符号限制?这两个哥们根本不用在服务端做,直接在前端做就可以了。所以我们只剩下一个问题了——昵称的唯一性。既然要检查昵称是否唯一,首先要有当前在线用户的昵称集合。不然去哪里查看昵称是否存在呢?所以需要全局维护一个数组,用来保存当前在线用户的昵称varusers=[];在这个数组中,找到用户通过登录事件传递的昵称。如果不存在,说明当前昵称合法,用户可以调用这个名字,则socket.nickname=nickname;//记录当前socket的昵称users.push(nickname);socket.emit('loginSuccess');//触发loginSuccess事件如果昵称已经存在,则触发登录失败事件,前端再做相应的交互即可。socket.emit('登录失败');接收用户发送的消息,根据约定的事件名编写服务端的监听程序;});这里调用的api是socket的广播事件,作用是将消息广播给除当前socket之外的所有socket。系统消息的处理剩下的工作就是处理系统消息。首先,需要明确哪些系统消息提示用户加入,提示用户离开,更新在线用户数。用户输入的昵称通过合法性校验后,系统提示新增用户.sockets.emit('system',nickname,users.length,'login');io.sockets.emit()的作用是对当前所有的socket触发一个事件,与socket.broadcast.emit()不同。按照上面的代码,编写用户离开时的广播事件:io.sockets.emit('system',nickname,users.length,'logout');但是应该写在哪里呢?这时候需要监听一个额外的断开连接事件socket.on('disconnect',function(){varindex=users.indexOf(socket.nickname);users.splice(index,1);//删除断开连接的用户的昵称来自全局数组用户io.sockets.emit('system',socket.nickname,users.length,'logout');});至此,一个基于Node.js的聊天室算是大功告成了,当然还有很多可以优化的地方,但是核心功能也就这些了,能看到这里的都是好人,因为看了自己写完的,感觉真的很像老太太的裹脚纸——臭臭的又长出来了,最后放个广告,具体什么就不说了,好奇的童鞋们自己扫一扫~