├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src ├── index.html └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebSocket Server from Scratch 2 | 3 | This is a WebSocket server implemented from scratch in Node.js without using any third-party libraries. The server accepts messages from clients and broadcasts them to all other connected clients. 4 | 5 | ### How to run 6 | 7 | 1. Clone this repository 8 | 9 | 2. Start the server 10 | 11 | ```bash 12 | npm start 13 | ``` 14 | 15 | 3. Open your browser & go to `localhost:3000` 16 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-from-scratch", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ws-from-scratch", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "nodemon": "^3.1.3" 13 | } 14 | }, 15 | "node_modules/anymatch": { 16 | "version": "3.1.3", 17 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 18 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 19 | "dev": true, 20 | "dependencies": { 21 | "normalize-path": "^3.0.0", 22 | "picomatch": "^2.0.4" 23 | }, 24 | "engines": { 25 | "node": ">= 8" 26 | } 27 | }, 28 | "node_modules/balanced-match": { 29 | "version": "1.0.2", 30 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 31 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 32 | "dev": true 33 | }, 34 | "node_modules/binary-extensions": { 35 | "version": "2.3.0", 36 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 37 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 38 | "dev": true, 39 | "engines": { 40 | "node": ">=8" 41 | }, 42 | "funding": { 43 | "url": "https://github.com/sponsors/sindresorhus" 44 | } 45 | }, 46 | "node_modules/brace-expansion": { 47 | "version": "1.1.11", 48 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 49 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 50 | "dev": true, 51 | "dependencies": { 52 | "balanced-match": "^1.0.0", 53 | "concat-map": "0.0.1" 54 | } 55 | }, 56 | "node_modules/braces": { 57 | "version": "3.0.3", 58 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 59 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 60 | "dev": true, 61 | "dependencies": { 62 | "fill-range": "^7.1.1" 63 | }, 64 | "engines": { 65 | "node": ">=8" 66 | } 67 | }, 68 | "node_modules/chokidar": { 69 | "version": "3.6.0", 70 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 71 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 72 | "dev": true, 73 | "dependencies": { 74 | "anymatch": "~3.1.2", 75 | "braces": "~3.0.2", 76 | "glob-parent": "~5.1.2", 77 | "is-binary-path": "~2.1.0", 78 | "is-glob": "~4.0.1", 79 | "normalize-path": "~3.0.0", 80 | "readdirp": "~3.6.0" 81 | }, 82 | "engines": { 83 | "node": ">= 8.10.0" 84 | }, 85 | "funding": { 86 | "url": "https://paulmillr.com/funding/" 87 | }, 88 | "optionalDependencies": { 89 | "fsevents": "~2.3.2" 90 | } 91 | }, 92 | "node_modules/concat-map": { 93 | "version": "0.0.1", 94 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 95 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 96 | "dev": true 97 | }, 98 | "node_modules/debug": { 99 | "version": "4.3.5", 100 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 101 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 102 | "dev": true, 103 | "dependencies": { 104 | "ms": "2.1.2" 105 | }, 106 | "engines": { 107 | "node": ">=6.0" 108 | }, 109 | "peerDependenciesMeta": { 110 | "supports-color": { 111 | "optional": true 112 | } 113 | } 114 | }, 115 | "node_modules/fill-range": { 116 | "version": "7.1.1", 117 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 118 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 119 | "dev": true, 120 | "dependencies": { 121 | "to-regex-range": "^5.0.1" 122 | }, 123 | "engines": { 124 | "node": ">=8" 125 | } 126 | }, 127 | "node_modules/fsevents": { 128 | "version": "2.3.3", 129 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 130 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 131 | "dev": true, 132 | "hasInstallScript": true, 133 | "optional": true, 134 | "os": [ 135 | "darwin" 136 | ], 137 | "engines": { 138 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 139 | } 140 | }, 141 | "node_modules/glob-parent": { 142 | "version": "5.1.2", 143 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 144 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 145 | "dev": true, 146 | "dependencies": { 147 | "is-glob": "^4.0.1" 148 | }, 149 | "engines": { 150 | "node": ">= 6" 151 | } 152 | }, 153 | "node_modules/has-flag": { 154 | "version": "3.0.0", 155 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 156 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 157 | "dev": true, 158 | "engines": { 159 | "node": ">=4" 160 | } 161 | }, 162 | "node_modules/ignore-by-default": { 163 | "version": "1.0.1", 164 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 165 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 166 | "dev": true 167 | }, 168 | "node_modules/is-binary-path": { 169 | "version": "2.1.0", 170 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 171 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 172 | "dev": true, 173 | "dependencies": { 174 | "binary-extensions": "^2.0.0" 175 | }, 176 | "engines": { 177 | "node": ">=8" 178 | } 179 | }, 180 | "node_modules/is-extglob": { 181 | "version": "2.1.1", 182 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 183 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 184 | "dev": true, 185 | "engines": { 186 | "node": ">=0.10.0" 187 | } 188 | }, 189 | "node_modules/is-glob": { 190 | "version": "4.0.3", 191 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 192 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 193 | "dev": true, 194 | "dependencies": { 195 | "is-extglob": "^2.1.1" 196 | }, 197 | "engines": { 198 | "node": ">=0.10.0" 199 | } 200 | }, 201 | "node_modules/is-number": { 202 | "version": "7.0.0", 203 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 204 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 205 | "dev": true, 206 | "engines": { 207 | "node": ">=0.12.0" 208 | } 209 | }, 210 | "node_modules/minimatch": { 211 | "version": "3.1.2", 212 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 213 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 214 | "dev": true, 215 | "dependencies": { 216 | "brace-expansion": "^1.1.7" 217 | }, 218 | "engines": { 219 | "node": "*" 220 | } 221 | }, 222 | "node_modules/ms": { 223 | "version": "2.1.2", 224 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 225 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 226 | "dev": true 227 | }, 228 | "node_modules/nodemon": { 229 | "version": "3.1.3", 230 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.3.tgz", 231 | "integrity": "sha512-m4Vqs+APdKzDFpuaL9F9EVOF85+h070FnkHVEoU4+rmT6Vw0bmNl7s61VEkY/cJkL7RCv1p4urnUDUMrS5rk2w==", 232 | "dev": true, 233 | "dependencies": { 234 | "chokidar": "^3.5.2", 235 | "debug": "^4", 236 | "ignore-by-default": "^1.0.1", 237 | "minimatch": "^3.1.2", 238 | "pstree.remy": "^1.1.8", 239 | "semver": "^7.5.3", 240 | "simple-update-notifier": "^2.0.0", 241 | "supports-color": "^5.5.0", 242 | "touch": "^3.1.0", 243 | "undefsafe": "^2.0.5" 244 | }, 245 | "bin": { 246 | "nodemon": "bin/nodemon.js" 247 | }, 248 | "engines": { 249 | "node": ">=10" 250 | }, 251 | "funding": { 252 | "type": "opencollective", 253 | "url": "https://opencollective.com/nodemon" 254 | } 255 | }, 256 | "node_modules/normalize-path": { 257 | "version": "3.0.0", 258 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 259 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 260 | "dev": true, 261 | "engines": { 262 | "node": ">=0.10.0" 263 | } 264 | }, 265 | "node_modules/picomatch": { 266 | "version": "2.3.1", 267 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 268 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 269 | "dev": true, 270 | "engines": { 271 | "node": ">=8.6" 272 | }, 273 | "funding": { 274 | "url": "https://github.com/sponsors/jonschlinkert" 275 | } 276 | }, 277 | "node_modules/pstree.remy": { 278 | "version": "1.1.8", 279 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 280 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 281 | "dev": true 282 | }, 283 | "node_modules/readdirp": { 284 | "version": "3.6.0", 285 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 286 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 287 | "dev": true, 288 | "dependencies": { 289 | "picomatch": "^2.2.1" 290 | }, 291 | "engines": { 292 | "node": ">=8.10.0" 293 | } 294 | }, 295 | "node_modules/semver": { 296 | "version": "7.6.2", 297 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", 298 | "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", 299 | "dev": true, 300 | "bin": { 301 | "semver": "bin/semver.js" 302 | }, 303 | "engines": { 304 | "node": ">=10" 305 | } 306 | }, 307 | "node_modules/simple-update-notifier": { 308 | "version": "2.0.0", 309 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", 310 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", 311 | "dev": true, 312 | "dependencies": { 313 | "semver": "^7.5.3" 314 | }, 315 | "engines": { 316 | "node": ">=10" 317 | } 318 | }, 319 | "node_modules/supports-color": { 320 | "version": "5.5.0", 321 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 322 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 323 | "dev": true, 324 | "dependencies": { 325 | "has-flag": "^3.0.0" 326 | }, 327 | "engines": { 328 | "node": ">=4" 329 | } 330 | }, 331 | "node_modules/to-regex-range": { 332 | "version": "5.0.1", 333 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 334 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 335 | "dev": true, 336 | "dependencies": { 337 | "is-number": "^7.0.0" 338 | }, 339 | "engines": { 340 | "node": ">=8.0" 341 | } 342 | }, 343 | "node_modules/touch": { 344 | "version": "3.1.1", 345 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", 346 | "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", 347 | "dev": true, 348 | "bin": { 349 | "nodetouch": "bin/nodetouch.js" 350 | } 351 | }, 352 | "node_modules/undefsafe": { 353 | "version": "2.0.5", 354 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 355 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 356 | "dev": true 357 | } 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ws-from-scratch", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "node src/server.js", 8 | "dev": "nodemon src/server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "description": "", 14 | "devDependencies": { 15 | "nodemon": "^3.1.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |