Socket.io 是什么,如何使用,与 WebSocket 的关系?
介绍
上篇我们讲到了 WebSocket 的使用,不太了解的小伙伴,建议先到我上篇文章了解下再来学习。
Socket.io是一个建立在 WebSocket 协议之上的库,他提供了低延迟、双向通信、基于事件(可以自定义双方约定好的事件)的功能,保证了使用中的稳定性和兼容性,比如用户使用的浏览器版本不支持,会使用 HTTP长轮询 实现:断线自动重连的功能。
与 WebSocket 区别
- Socket.io 虽然建立在 WebSocket 协议之上,但不是 WebSocket 的实现。
- 目前仍有些许浏览器无法建立WebSocket连接,使用 Socket.io 可以做兼容性处理,转为 HTTP长轮询。
- 服务器和客户端之间的WebSocket连接可能会中断,双方都不知道连接的中断状态。Socket.io 自带一个心跳机制,它可以定期检查连接的状态,WebSocket 需要手动实现。
- 虽然 Socket.io 确实是在客户端支持的情况下使用 WebSocket 进行传输,但是它为每个数据包添加了额外的 元数据。这就是 WebSocket 客户端无法成功连接到 Socket.io 服务端,Socket.io 客户端也无法连接到普通的WebSocket服务器的原因。所以双方要同时规定好使用 websocket 还是 Socket.io,不能混用。
- Socket.io 提供了支持派发事件和监听时间的便捷功能,以下是官网案例:
| socket.emit("hello", "world", (response) => { |
| console.log(response); |
| }); |
| socket.on("hello", (arg, callback) => { |
| console.log(arg); |
| callback("got it"); |
| }); |
复制
上面代码中的"hello"就是双方约定好的事件,平常项目开发中一般会在前面加上 $,表示自定义事件。
| |
| io.on("connection", (socket) => { |
| socket.broadcast.emit("hello", "world"); |
| }); |
| |
| socket.on('message', (message) => { |
| ws.clients.forEach((client) => { |
| client.send({ |
| type: "hello", |
| message |
| }); |
| }) |
| }) |
复制
使用 Socket.io 实现一个简易聊天室
- 我这里前端使用 vue3+TS,后端 node(这是肯定的,只支持node)
- 先看成品展示,简单做了一下,样式请不要在乎。。。


- 以下是代码部分
- 前端
| <template> |
| <div class="container"> |
| <div class="users-area"> |
| |
| <input type="text" placeholder="搜索"> |
| <ul class="users-list"> |
| <li v-for="u in users" :key="u">{{ u }}</li> |
| </ul> |
| </div> |
| <div class="msg-area"> |
| <div class="message-area"> |
| <div class="item" :class="{ self: self === item.name }" v-for="(item, ind) in msgList" :key="ind"> |
| <span class="name">{{ item.name }}</span> |
| <p class="message">{{ item.message }}</p> |
| </div> |
| </div> |
| <div class="input-area"> |
| <textarea v-model="message" @keydown.enter="sendMsg"></textarea> |
| </div> |
| </div> |
| </div> |
| </template> |
复制
| <script setup lang="ts"> |
| import { defineProps, watch, ref, defineEmits } from 'vue'; |
| const message = ref(''); |
| const emits = defineEmits(['chat']) |
| type Props = { |
| users?: any[]; |
| msgList?: { name: string, message: string}[]; |
| self: string |
| } |
| const props = withDefaults(defineProps<Props>(), { |
| users: () => [], |
| msgList: () => [] |
| }) |
| |
| const sendMsg = () => { |
| const val = message.value.trim(); |
| if (val) { |
| message.value = ''; |
| emits('chat', { name: props.self, message: val }); |
| } |
| } |
| </script> |
复制
| <style scoped> |
| .container { |
| display: flex; |
| width: 550px; |
| height: 500px; |
| border-radius: 8px; |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); |
| background-color: rgb(235, 235, 235); |
| overflow: hidden; |
| } |
| |
| .users-area { |
| width: 150px; |
| border-right: 1px solid #ccc; |
| line-height: 30px; |
| overflow: auto; |
| flex: 0 0 auto; |
| } |
| .users-area input { |
| margin-top: 10px; |
| width: 80%; |
| border: none |
| } |
| .users-area input:focus { |
| outline: 1px solid #aaa |
| } |
| |
| .users-area p { |
| text-align: center; |
| border-bottom: 1px solid #ccc; |
| } |
| |
| .users-area .users-list { |
| padding: 0; |
| margin: 10px 0; |
| list-style: none; |
| border-top: 1px solid #ccc; |
| } |
| |
| .users-list li { |
| padding: 0 10px; |
| margin: 10px 0; |
| font-size: 12px; |
| border-bottom: 1px solid #ccc; |
| background: rgb(220, 220, 220); |
| } |
| |
| .msg-area { |
| display: flex; |
| flex-direction: column; |
| flex: 1; |
| background-color: rgb(245, 245, 245); |
| } |
| |
| .message-area { |
| height: 75%; |
| padding: 1em; |
| font-size: 14px; |
| line-height: 1.3; |
| overflow-y: scroll; |
| } |
| |
| .item { |
| float: left; |
| max-width: 70%; |
| clear: both; |
| margin-bottom: 1em; |
| } |
| |
| .name { |
| font-size: 12px; |
| color: #333; |
| } |
| |
| .message { |
| border-radius: 6px; |
| padding: 10px; |
| margin: 4px 0; |
| background-color: #fff |
| } |
| |
| .item.self { |
| float: right; |
| } |
| |
| .self .message { |
| background-color: rgb(137, 217, 97); |
| } |
| |
| .self .name { |
| text-align: right; |
| } |
| |
| .input-area { |
| flex: 1; |
| border-top: 1px solid #ccc; |
| } |
| |
| .input-area textarea { |
| width: 100%; |
| height: 100%; |
| padding: 10px 20px; |
| border: none; |
| outline: none; |
| } |
| </style> |
复制
| <template> |
| <WeChat :self="self" :msgList="msgList" :users="users" @chat="handleChat" /> |
| </template> |
| |
| <script setup lang='ts'> |
| import WeChat from '@/components/WeChat.vue'; |
| import { io, Socket } from 'socket.io-client'; |
| import { ref, onMounted, onUnmounted } from 'vue'; |
| |
| interface IChatContent { |
| name: string; |
| message: string |
| } |
| |
| const msgList = ref<IChatContent[]>([]); |
| const users = ref<string[]>([]); |
| const self = ref(''); |
| const socket = ref<Socket | null>(null); |
| |
| onMounted(() => { |
| socket.value = io('http://localhost:3000'); |
| // 接受消息 |
| socket.value.on('$messages', (data: IChatContent) => { |
| msgList.value.push(data); |
| }); |
| // 监听加入聊天室的用户 |
| socket.value.on('$users', (data: string[]) => { |
| users.value = data; |
| }); |
| // 监听其他人发送的消息 |
| socket.value.on('$msgList', (data: IChatContent[]) => { |
| msgList.value = data; |
| }); |
| }) |
| const handleChat = (data: IChatContent) => { |
| msgList.value.push(data); |
| // 将当前聊天内容发送给服务器,通知其他人 |
| (socket.value as Socket).emit('$messages', data.message); |
| } |
| /** |
| * 组件卸载时断开连接 |
| */ |
| onUnmounted(() => { |
| (socket.value as Socket).disconnect(); |
| }) |
| </script> |
复制
| import { Server } from "socket.io"; |
| |
| const io = new Server({ |
| path: '/', |
| cors: '*' |
| }); |
| const usersList = []; |
| const chatContentList = []; |
| let ind = 0; |
| |
| io.on("connection", (socket) => { |
| const userName = '成员' + ++ind; |
| usersList.push(userName); |
| |
| socket.emit('$users', usersList); |
| socket.on('$messages', (message) => { |
| const content = { |
| name: userName, |
| message |
| } |
| chatContentList.push(content); |
| |
| socket.broadcast.emit('$messages', content); |
| }) |
| |
| |
| socket.on('disconnect', () => { |
| |
| usersList.splice(usersList.indexOf(userName), 1); |
| |
| socket.broadcast.emit('$users', usersList); |
| }); |
| }); |
| |
| io.listen(3000); |
复制
- 更多细节与功能请参考官网:https://socket.io/docs/v4/tutorial/introduction
- 欢迎各位小伙伴指出意见,觉得不错的话不要忘记留下个赞哦,你的鼓励是我更新的动力,谢谢。