服務器源碼地址:https://github.com/ermu592275254/chat-socket 網頁源碼地址:https://github.com/ermu592275254/chat-socket
使用nodejs搭建後臺,由於是一個單頁應用,而且先後端通訊使用了webSocket,全部只用http
模塊搭建一個簡單的服務器,未使用koa、express
等web框架。前端
使用socket.io
實現webSocket,前端經過import socket.io 的方式會出現不斷重連的狀況,因而使用script
方式實現。vue
const io = require('socket.io-client'); // or with import syntax import io from 'socket.io-client'; // or script <script src="/socket.io/socket.io.js"></script> <script> const socket = io('http://localhost'); </script>
使用mongoose
操做mongodb。mongodb這類非關係型數據庫,功能較關係型數據庫閹割了許多。主要表如今複雜的sql語句、事務支持等。node
使用vue以及vue的衍生產品,同時用到bootstarp做爲樣式框架。簡單兼容了PC和移動。(PC僅支持chrome,在firefox、ie等瀏覽器中,會出現樣式、佈局混亂的狀況)。git
經過用戶名和socketId進行匹配。保存用戶每次登陸的socketId,當對方在線時,將此信息經過socketId發送給對方。不在線僅保存到數據庫,用戶上線便可在私聊中查看。目前不支持消息通知,也不支持未讀消息github
...// 每次登陸都將socketId替換爲當前登陸的socketId userModel.update({username: data.username}, {socketId: socket.id}).then(res => { socket.emit('login', user); }).catch(err => { console.log(err); socket.emit('err', 'update user socketId was failed'); }); ...
chatModel.findOne({sendTime: time}).populate('sender receiver').then(newChat=>{ let receiverData = { receiver: data.sender, data: newChat }; // 若是對方在線就發送給對方 if (io.sockets.connected[user.socketId]) { io.sockets.connected[user.socketId].emit('newMessage', receiverData); } let senderData = { receiver: data.receiver, data: newChat }; // 同時也發送給本身(也可直接在前端添加,後端不發送) io.sockets.connected[socket.id].emit('newMessage', senderData); }).catch(err=>{ io.sockets.connected[socket.id].emit('err', 'can`t find the newMessage') })
經過broadcast
實現組發送。將羣、羣對應的聊天記錄保存在數據庫。用戶進入羣聊,則將其加入到對應的broadcast中。web
socket.on('joinRoom', function(data) { if (!common.checkData(data)) { io.sockets.connected[socket.id].emit('err', 'request params Can`t be empty'); return; } // 加入對應的羣聊 socket.join(data.groupName, function() { let roomName = Object.keys(socket.rooms); io.to(data.groupName, `${data.username} has joined the room`); socket.broadcast.in('data.groupName').emit('newUserJoin', { groupName: data.groupName, username: data.username }) }); })
groupChatModel.findOne({'sendTime': time}).populate('sender').then(res=>{ if(res){ // 發送給本身 io.sockets.connected[socket.id].emit('newMsgOfGroup', res); // 將消息發送給羣裏的全部人除了本身 socket.broadcast.in(data.groupName).emit('newMsgOfGroup', res); } else { io.sockets.connected[socket.id].emit('err', 'the message data is null'); }
一樣使用webSocket,將頭像ID保存在用戶信息表中,將圖片文件保存在服務器static文件夾中。sql
uploadIcon(){ let file = this.$refs.uploadEl.files[0]; console.log(file); if(file.size > 100000){ this.Toast('文件大小不能超過1M'); this.$refs.uploadEl.value = ''; return; } let data = { username: this.user.username, file: file, type: file.type.split('/')[1] }; socket.emit('uploadUserIcon', data); this.$refs.uploadEl.value = ''; }
socket.on('uploadUserIcon', function(data) { let time = new Date().getTime(); let savePath = `/static/userIcon/${time}.${data.type}`; let hostPath = 'http://' + host + ':' + port; // 經過fs模塊操做 fs.writeFile('.'+ savePath, data.file, function(err) { if (err) { console.log(err); io.sockets.connected[socket.id].emit('err', 'save userIcon failed'); } else { userModel.update({username: data.username}, {$set: {userIcon: hostPath + savePath}}).then(res => { userModel.findOne({username: data.username}).then(user=>{ io.sockets.connected[socket.id].emit('uploadUserIcon', { user: user, message: 'upload userIcon success' }); }).catch(err =>{ io.sockets.connected[socket.id].emit('err', 'find userInfo failed'); }); }).catch(err => { io.sockets.connected[socket.id].emit('err', 'save userIcon path failed'); }) } }) });
將用戶名做爲惟一值。註冊時不能註冊已存在的用戶名。登陸支持自動登陸,將密碼保存在localStorage中。mongodb
最後: 這是本菜雞陸陸續續作了一年的項目,屢次放棄又從新拾起。代碼寫得不堪入目,沒有精力和激情再去作優化了。暫時先這樣吧......chrome