├── .gitignore ├── src ├── nodejs-websocket │ ├── websocket.js │ ├── ws-client.js │ ├── express.js │ ├── ws-server.js │ └── wsc.html └── webrtc-web │ └── local │ ├── index.html │ ├── index.js │ └── index.tsx ├── .prettierrc.json ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | package-lock.json 9 | test.* -------------------------------------------------------------------------------- /src/nodejs-websocket/websocket.js: -------------------------------------------------------------------------------- 1 | // express 框架子路由匹配 websocket 2 | 3 | var express = require('express') 4 | var router = express.Router() 5 | 6 | router.ws('/test-ws', (ws, req) => { 7 | ws.on('message', msg => { 8 | console.log(msg) 9 | ws.send('服务端回复:' + msg) 10 | }) 11 | }) 12 | 13 | module.exports = router 14 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": false, 4 | "arrowParens": "avoid", 5 | "bracketSpacing": true, 6 | "jsxBracketSameLine": true, 7 | "requirePragma": false, 8 | "overrides": [ 9 | { 10 | "files": ["*.json"], 11 | "options": { 12 | "parser": "json-stringify" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/nodejs-websocket/ws-client.js: -------------------------------------------------------------------------------- 1 | // ws 模块客户端 2 | 3 | const WebSocket = require('ws') 4 | var ws = new WebSocket('ws://localhost:8080') 5 | 6 | ws.on('open', () => { 7 | console.log('客户端已连接') 8 | }) 9 | 10 | ws.on('message', data => { 11 | console.log('客户端收到消息: ' + data) 12 | ws.close() 13 | }) 14 | 15 | ws.on('close', () => { 16 | console.log('连接关闭') 17 | }) 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.3", 13 | "express-ws": "^5.0.2", 14 | "react": "^17.0.2", 15 | "ws": "^8.3.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 杨成功的文章源码 2 | 3 | 你好,我是掘金作者[杨成功](https://juejin.cn/user/1169536100339101),这里是我的文章源码库。 4 | 5 | 本人平时喜欢写技术文章,文章里的结论都有代码片段佐证。这些代码并不是从官网或者某个地方粘过来的 Demo,每一行代码,都要经过我自己的实践,看到运行结果符合,我才会把它写到文章里。 6 | 7 | 为了方便大家边看文章边实践,我把所有文章中涉及的源码都整理起来放到这里,这样小伙伴们可以拿到源码直接运行,与文章对比学习效率更高。 8 | 9 | 下面是文章标题对应的源码导航: 10 | 11 | - [前端架构师破局技能,NodeJS 落地 WebSocket 实践](./src/nodejs-websocket) 12 | - [音视频通信加餐 —— WebRTC 一肝到底](./src/webrtc-web/local) 13 | 14 | 欢迎大家查阅和试验,如果碰到疑惑的地方,加我微信 `ruidoc` 讨论。 15 | 16 | 最后,如果文章/源码对你有帮助,欢迎 `start` 收藏,你的鼓励是我最大的动力 🙏 17 | -------------------------------------------------------------------------------- /src/nodejs-websocket/express.js: -------------------------------------------------------------------------------- 1 | // 在 express 框架中集成 websocket 2 | 3 | var express = require('express') 4 | var app = express() 5 | var wsServer = require('express-ws')(app) 6 | var webSocket = require('./websocket.js') 7 | 8 | app.locals.wss = wsServer.getWss() 9 | 10 | app.ws('/test-ws', (ws, req) => { 11 | ws.on('message', msg => { 12 | console.log(msg) 13 | ws.send('服务端回复:' + msg) 14 | }) 15 | }) 16 | 17 | app.use('/websocket', webSocket) 18 | 19 | app.listen(9700, () => { 20 | console.log('正在监听 9700 端口...') 21 | }) 22 | -------------------------------------------------------------------------------- /src/nodejs-websocket/ws-server.js: -------------------------------------------------------------------------------- 1 | // ws 模块服务端 2 | 3 | const { WebSocketServer } = require('ws') 4 | 5 | const wss = new WebSocketServer({ 6 | port: 8080, 7 | }) 8 | 9 | wss.on('connection', (ws, req) => { 10 | // console.log('客户端已连接:', ws) 11 | ws.on('message', data => { 12 | console.log('收到客户端发送的消息:', data) 13 | }) 14 | // 获取已连接的客户端 15 | console.log('已连接的客户端:', wss.clients.size) 16 | // 向当前客户端发送消息 17 | ws.send('我是服务端') 18 | }) 19 | 20 | // 连接验证 21 | wss.on('upgrade', (request, socket) => { 22 | console.log('请求前') 23 | }) 24 | -------------------------------------------------------------------------------- /src/webrtc-web/local/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebRTC本地通信 8 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/nodejs-websocket/wsc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebSocket 8 | 9 | 10 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/webrtc-web/local/index.js: -------------------------------------------------------------------------------- 1 | var peerA = null, 2 | peerB = null 3 | 4 | var videoElA = document.getElementById('elA') 5 | var videoElB = document.getElementById('elB') 6 | 7 | const onStart = async () => { 8 | try { 9 | let stream = await navigator.mediaDevices.getUserMedia({ 10 | audio: true, 11 | video: true, 12 | }) 13 | videoElA.srcObject = stream // 在 video 标签上播放媒体流 14 | peerInit(stream) // 初始化连接 15 | } catch (error) { 16 | console.log('error:', error) 17 | } 18 | } 19 | 20 | const peerInit = stream => { 21 | // 1. 创建连接实例 22 | peerA = new RTCPeerConnection() 23 | peerB = new RTCPeerConnection() 24 | // 2. 添加视频流轨道 25 | stream.getTracks().forEach(track => { 26 | peerA.addTrack(track, stream) 27 | }) 28 | // 添加 candidate 29 | peerA.onicecandidate = event => { 30 | if (event.candidate) { 31 | peerB.addIceCandidate(event.candidate) 32 | } 33 | } 34 | // 检测连接状态 35 | peerA.onconnectionstatechange = event => { 36 | if (peerA.connectionState === 'connected') { 37 | console.log('对等连接成功!') 38 | } 39 | } 40 | // 监听数据传来 41 | peerB.ontrack = event => { 42 | const [remoteStream] = event.streams 43 | videoElB.srcObject = remoteStream 44 | } 45 | // 互换sdp认证 46 | transSDP() 47 | } 48 | 49 | const transSDP = async () => { 50 | // 1. 创建 offer 51 | let offer = await peerA.createOffer() 52 | await peerB.setRemoteDescription(offer) 53 | // 2. 创建 answer 54 | let answer = await peerB.createAnswer() 55 | await peerB.setLocalDescription(answer) 56 | // 3. 发送端设置 sdp 57 | await peerA.setLocalDescription(offer) 58 | await peerA.setRemoteDescription(answer) 59 | } 60 | -------------------------------------------------------------------------------- /src/webrtc-web/local/index.tsx: -------------------------------------------------------------------------------- 1 | // react 版本的 demo 2 | import React, { useEffect, useRef } from 'react' 3 | 4 | const LocalPage = () => { 5 | const videoElA = useRef(null), 6 | videoElB = useRef(null) 7 | var peerA: RTCPeerConnection = null, 8 | peerB: RTCPeerConnection = null 9 | 10 | const onStart = async () => { 11 | try { 12 | let stream = await navigator.mediaDevices.getUserMedia({ 13 | audio: true, 14 | video: true, 15 | }) 16 | if (videoElA.current) { 17 | videoElA.current.srcObject = stream // 在 video 标签上播放媒体流 18 | } 19 | peerInit(stream) // 初始化连接 20 | } catch (error) { 21 | console.log('error:', error) 22 | } 23 | } 24 | 25 | const peerInit = (stream: MediaStream) => { 26 | // 1. 创建连接实例 27 | peerA = new RTCPeerConnection() 28 | peerB = new RTCPeerConnection() 29 | // 2. 添加视频流轨道 30 | stream.getTracks().forEach(track => { 31 | peerA.addTrack(track, stream) 32 | }) 33 | // 添加 candidate 34 | peerA.onicecandidate = event => { 35 | if (event.candidate) { 36 | peerB.addIceCandidate(event.candidate) 37 | } 38 | } 39 | // 检测连接状态 40 | peerA.onconnectionstatechange = event => { 41 | if (peerA.connectionState === 'connected') { 42 | console.log('对等连接成功!') 43 | } 44 | } 45 | // 监听数据传来 46 | peerB.ontrack = async event => { 47 | const [remoteStream] = event.streams 48 | videoElB.current.srcObject = remoteStream 49 | } 50 | // 互换sdp认证 51 | transSDP() 52 | } 53 | 54 | const transSDP = async () => { 55 | // 1. 创建 offer 56 | let offer = await peerA.createOffer() 57 | await peerB.setRemoteDescription(offer) 58 | // 2. 创建 answer 59 | let answer = await peerB.createAnswer() 60 | await peerB.setLocalDescription(answer) 61 | // 3. 发送端设置 sdp 62 | await peerA.setLocalDescription(offer) 63 | await peerA.setRemoteDescription(answer) 64 | } 65 | 66 | useEffect(() => {}, []) 67 | 68 | return ( 69 |
70 | 71 | 72 | 73 |
74 | ) 75 | } 76 | 77 | export default LocalPage 78 | --------------------------------------------------------------------------------