HTTP的局限
传统HTTP是一种单向请求-响应协议,客户端发送请求后,服务器才会响应并返回相应的数据
这种情况下,客户端需要主动发送请求才能获取服务器上的资源,而且每次请求都需要重新建立连接,这种方式在实时通信和持续获取资源的场景下效率较低

而Socket提供了客户端和服务器之间即时的双工通信能力,可以实时、更快速、更低延迟地的传递数据
这种情况下,数据的传输和相应几乎是实时完成的,不需要轮询或定时发送请求

Socket.IO
大型开发时通常不会使用原生WebSocket,而是使用Socket.IO
Socket.IO是一个基于事件驱动的实时通信框架,用于构建实时应用程序,它提供了双向、低延迟的通信能力,使得服务器和客户端可以实时地发送和接收数据
Socket.IO的特点
- 实时性:作为封装后的WebSocket,能实现双工实时通信
- 事件驱动
- 使用事件驱动的编程模型,可以让服务器和客户端通过触发事件来发送和接收数据
- 可以轻松地构建实时的应用程序,例如聊天应用、实时协作工具等
- 跨平台支持
- 可以在多个平台上使用,包括浏览器、服务器和移动设备等
- 提供了对多种编程语言和框架的支持,如JavaScript、Node.js、Python、Java等
- 容错性:具有容错能力,当WebSocket连接不可用时,它可以自动降级到其他传输机制,例如HTTP长轮询
- 扩展性
- 支持水平扩展,可以将应用程序扩展到多个服务器,并实现事件的广播和传递
- 这使得应用程序可以处理大规模的并发连接,实现高可用性和高性能
依赖
编码
server.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import http from 'node:http' import { Server } from 'socket.io'
const server = http.createServer()
const io = new Server(server, { cors: true })
const groups = {}
io.on('connection', socket => { console.log('a user connected') socket.on('join', ({name, room}) => {
socket.join(room)
if(groups[room]) { groups[room].push({name, id: socket.id}) } else { groups[room] = [{name, id: socket.id}] }
socket.emit('message', {user: 'admin', text: `${name} 加入了聊天室`}) socket.emit('groups', groups) socket.broadcast.emit('groups', groups) })
socket.on('message', ({text, room, user}) => { socket.broadcast.to(room).emit('message', {user, text}) })
socket.on('disconnect', () => { Object.keys(groups).forEach(key => { let leaveUser = groups[key].find(item => item.id === socket.id) if(leaveUser) { groups[key] = groups[key].filter(item => item.id !== socket.id) socket.broadcast.to(leaveUser.room).emit('message', {user: 'admin', text: `${leaveUser.name} 退出了聊天室`}) } }) }) })
server.listen(11451, () => { console.log('listening on *:11451') })
|
index.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; }
html, body, .room { height: 100%; width: 100%; display: flex; justify-content: center; align-items: center; background-color: #2b313d;
}
.room { width: 95%; height: 95%; overflow: hidden; border-radius: 20px; }
.room_inner { width: 100%; height: 100%; display: flex; }
.left { width: 300px; margin-right: 10px; background-image: linear-gradient(to top, #6a85b6 0%, #bac8e0 100%); }
.right { background-image: linear-gradient(to top, #accbee 0%, #e7f0fd 100%); overflow: hidden; flex: 1; display: flex; flex-direction: column; }
.header { background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px; box-sizing: border-box; font-size: 20px; }
.main { flex: 1; padding: 10px; box-sizing: border-box; font-size: 20px; overflow: auto; }
.main-chat { color: #2b313d; }
.footer { min-height: 160px; background-image: linear-gradient(to top, #e6e9f055 0%, #eef1f555 100%); border-radius: 20px; margin: 10px; }
.footer .ipt { width: 100%; height: 100%; color: #2b313d; outline: none; font-size: 20px; padding: 10px; box-sizing: border-box; }
.groups { height: 100%; overflow: auto; position: relative; }
.groups::-webkit-scrollbar { position: absolute; width: 5px; opacity: 0; }
.groups::-webkit-scrollbar-thumb { width: 10px; background-image: linear-gradient(to top, #30cfd0 0%, #330867 100%); }
.groups::-webkit-scrollbar-track { opacity: 0; }
.groups:hover::-webkit-scrollbar { opacity: 1; }
.groups-items { margin: 10px; height: 50px; width: calc(100% - 20px); border-radius: 20px; background-image: linear-gradient(to right, #5f72bd 0%, #9b23ea 100%); display: flex; align-items: center; justify-content: center; color: white; } </style> </head> <div class="room"> <div class="room_inner"> <div class="left"> <div class="groups">
</div> </div> <div class="right"> <header class="header">聊天室</header> <main class="main">
</main> <footer class="footer"> <div class="ipt" contenteditable></div> </footer> </div> </div>
</div>
<body> <script type="module"> import io from "https://cdn.socket.io/4.7.4/socket.io.esm.min.js" const main = document.querySelector('.main') const groupsEl = document.querySelector('.groups')
const sendMessage = (message) => { const div = document.createElement('div') div.className = 'main-chat' div.innerHTML = `${message.user}: ${message.text}` main.appendChild(div) }
const name = prompt('请输入名字') const room = prompt('请输入房间名')
const socket = io('ws://localhost:11451')
document.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault() const text = document.querySelector('.footer .ipt').innerText if (text.trim() === '') return socket.emit('message', {text, room, user: name}) sendMessage({user: name, text}) document.querySelector('.footer .ipt').innerText = '' } })
socket.on('connect', () => {
socket.emit('join', {name, room}) socket.on('message', (message) => { sendMessage(message) }) socket.on('groups', (groups) => { groupsEl.replaceChildren() Object.keys(groups).forEach((key) => { const div = document.createElement('div') div.className = 'groups-items' div.innerHTML = `房间:${key} 人数:${groups[key].length}` groupsEl.appendChild(div) }) }) }) </script> </body>
</html>
|

顺便安利一个分享180种渐变色的小网站:免费的渐变背景CSS3样式