笔者第一次上网,学校网络室给定制的首页是一个红泥巴的聊天室。这回去看了下,卧槽还没倒闭:
果然是经得起时间考验的项目了。
socket实现——一个即时终端聊天室
net模块提供一个异步api能够创建基于流的tcp服务器,客户端和服务端建立连接之后,服务器可以获得一个双工socket对象,服务器可以保存socket对象列表,在接受某客户端消息时,推送给其他客户端。
代码语言:javascript复制// socket.js
const net=require('net');
const chatServer=net.createServer();
const clientList=[];
chatServer.on('connection',client=>{
client.write('Hin');
clientList.push(client);
// 当接收到数据时:
client.on('data',data=>{
console.log(`receive:${data.toString()}`);
clientList.forEach(v=>{
v.write(data);
})
})
});
chatServer.listen(9000);
如何测试呢?这里用到telnet:
telnet
本来是mac os 10.13之前的内置服务,在高级版本中,需要本地安装一下:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install telnet
安装完成之后:
代码语言:javascript复制telnet localhost 9000
打印出'hi'
假设再建立一个客户端:同样也能收到消息。
网络聊天室
http的一个重要弱点在于,只能拉取,不能主动推送。所以后端扯皮时会说:'"你不穿东西给我,我就没东西给你。"这个时候只好做轮询(苦了前端)。
从项目角度说,HTTP协议是非持久化的,单向的网络协议,在建立连接后只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。当需要即时通讯时,通过轮询在特定的时间间隔(如1秒),由浏览器向服务器发送Request请求,然后将最新的数据返回给浏览器。这样的方法最明显的缺点就是需要不断的发送请求,而且通常HTTP request的Header是非常长的,为了传输一个很小的数据 需要付出巨大的代价,是很不合算的,占用了很多的宽带
但如果有了socket.io,事情就好办多了。
Socket.io是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用。它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5
代码语言:javascript复制npm i socket.io -S
在后端基本上不需要做什么处理:
代码语言:javascript复制// 服务端:chat-socketio.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function (req, res) {
res.sendFile(__dirname '/index.html');
});
io.on('connection', function (socket) {
console.log('a user connected');
io.emit('chat message', {
type:'notice',
time:new Date().toLocaleString(),
name:'新用户',
msg:'加入了群聊'
});
//响应某用户发送消息
socket.on('chat message', function (msg) {
console.log('chat message:' msg);
// 广播给所有人
io.emit('chat message', msg);
// 广播给除了发送者外所有人
// socket.broadcast.emit('chat message', msg)
});
socket.on('disconnect', function () {
io.emit('chat message', {
msg:'用户退出了群聊',
type:'notice',
name:'有用户',
time:new Date().toLocaleString(),
});
console.log('user disconnected');
});
});
http.listen(3000, function () {
console.log('listening on *:3000');
});
前端:
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style type="text/css">
/* blabla */
</style>
</head>
<body>
<div id="root">
<!-- <input type="file" name="file" id="upload"> -->
<div id="dialog">
</div>
<div id="control">
<input id="username" type="text">
<textarea id="msg" cols="30" rows="10"></textarea>
<button id="send">发送</button>
</div>
</div>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
var dialog = document.querySelector('#dialog');
var send = document.querySelector('#send');
var msg = document.querySelector('#msg');
var socket = io();
send.addEventListener('click', (e) => {
socket.emit("chat message", {
name:document.querySelector('#username').value,
msg:msg.value,
time:new Date().toLocaleString(),
type:'normal'
});
})
socket.on("chat message", function (msg) {
let html=``;
switch (msg.type) {
case 'notice':
html=`<div class="notice">
${msg.name} ${msg.msg} <small>${msg.time}</small>
</div>`;
break;
default:
html=`<div class="msg-wrap">
<div class="name">${msg.name} <small>${msg.time}</small></div>
<div class=""msg">${msg.msg}</div>
</div>`
break;
}
dialog.innerHTML =html;
dialog.scrollTop = dialog.scrollHeight;
});
</script>
</body>
</html>
那么就实现了。