[{"data":1,"prerenderedAt":2146},["ShallowReactive",2],{"page-\u002Fnetwork\u002F探究socketio事件驱动的双向通信库":3},{"id":4,"title":5,"body":6,"description":2134,"extension":2135,"meta":2136,"navigation":113,"path":2142,"seo":2143,"stem":2144,"__hash__":2145},"content\u002Fnetwork\u002F探究SocketIO事件驱动的双向通信库.md","探究SocketIO事件驱动的双向通信库",{"type":7,"value":8,"toc":2132},"minimark",[9,29,35,38,414,417,601,616,825,843,862,884,903,1040,1056,1064,1075,1105,1135,1145,1165,1168,1827,1830,1921,1924,2066,2128],[10,11,12,19,20,24,25,28],"p",{},[13,14,18],"a",{"href":15,"rel":16},"https:\u002F\u002Fsocket.io\u002F",[17],"nofollow","Socket.IO"," 是一个基于事件驱动的实时双向通信库，它的核心价值在于",[21,22,23],"strong",{},"封装了 WebSocket 的复杂性","，并提供了",[21,26,27],{},"自动降级、自动重连、房间（Room）分组","等开箱即用的高级功能。",[10,30,31,34],{},[13,32,18],{"href":15,"rel":33},[17]," 的使用需要服务端和客户端配合。",[10,36,37],{},"在Nodejs服务端上，你需要将SocketIO实例挂载到一个HTTP服务器上：",[39,40,45],"pre",{"className":41,"code":42,"language":43,"meta":44,"style":44},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F 使用 ES Modules 语法\nimport express from 'express';\nimport { createServer } from 'node:http';\nimport { Server } from 'socket.io';\n\nconst app = express();\nconst server = createServer(app);\n\u002F\u002F 初始化 Socket.IO，并传入 HTTP server 实例\nconst io = new Server(server);\n\n\u002F\u002F 监听客户端连接\nio.on('connection', (socket) => {\n  console.log('一个用户已连接，ID:', socket.id);\n\n  \u002F\u002F 监听客户端发来的自定义事件 'chat message'\n  socket.on('chat message', (msg) => {\n    console.log('收到消息:', msg);\n    \u002F\u002F 将消息广播给所有已连接的客户端\n    io.emit('chat message', msg);\n  });\n  \n  \u002F\u002F 监听客户端断开连接\n  socket.on('disconnect', () => {\n    console.log('用户已断开:', socket.id);\n  });\n});\n\nserver.listen(3000, () => {\n  console.log('服务端运行在 http:\u002F\u002Flocalhost:3000');\n});\n","ts","",[46,47,48,57,78,93,108,115,135,151,157,176,181,187,218,235,240,246,270,286,292,307,313,319,325,344,358,363,369,374,394,409],"code",{"__ignoreMap":44},[49,50,53],"span",{"class":51,"line":52},"line",1,[49,54,56],{"class":55},"sJ8bj","\u002F\u002F 使用 ES Modules 语法\n",[49,58,60,64,68,71,75],{"class":51,"line":59},2,[49,61,63],{"class":62},"szBVR","import",[49,65,67],{"class":66},"sVt8B"," express ",[49,69,70],{"class":62},"from",[49,72,74],{"class":73},"sZZnC"," 'express'",[49,76,77],{"class":66},";\n",[49,79,81,83,86,88,91],{"class":51,"line":80},3,[49,82,63],{"class":62},[49,84,85],{"class":66}," { createServer } ",[49,87,70],{"class":62},[49,89,90],{"class":73}," 'node:http'",[49,92,77],{"class":66},[49,94,96,98,101,103,106],{"class":51,"line":95},4,[49,97,63],{"class":62},[49,99,100],{"class":66}," { Server } ",[49,102,70],{"class":62},[49,104,105],{"class":73}," 'socket.io'",[49,107,77],{"class":66},[49,109,111],{"class":51,"line":110},5,[49,112,114],{"emptyLinePlaceholder":113},true,"\n",[49,116,118,121,125,128,132],{"class":51,"line":117},6,[49,119,120],{"class":62},"const",[49,122,124],{"class":123},"sj4cs"," app",[49,126,127],{"class":62}," =",[49,129,131],{"class":130},"sScJk"," express",[49,133,134],{"class":66},"();\n",[49,136,138,140,143,145,148],{"class":51,"line":137},7,[49,139,120],{"class":62},[49,141,142],{"class":123}," server",[49,144,127],{"class":62},[49,146,147],{"class":130}," createServer",[49,149,150],{"class":66},"(app);\n",[49,152,154],{"class":51,"line":153},8,[49,155,156],{"class":55},"\u002F\u002F 初始化 Socket.IO，并传入 HTTP server 实例\n",[49,158,160,162,165,167,170,173],{"class":51,"line":159},9,[49,161,120],{"class":62},[49,163,164],{"class":123}," io",[49,166,127],{"class":62},[49,168,169],{"class":62}," new",[49,171,172],{"class":130}," Server",[49,174,175],{"class":66},"(server);\n",[49,177,179],{"class":51,"line":178},10,[49,180,114],{"emptyLinePlaceholder":113},[49,182,184],{"class":51,"line":183},11,[49,185,186],{"class":55},"\u002F\u002F 监听客户端连接\n",[49,188,190,193,196,199,202,205,209,212,215],{"class":51,"line":189},12,[49,191,192],{"class":66},"io.",[49,194,195],{"class":130},"on",[49,197,198],{"class":66},"(",[49,200,201],{"class":73},"'connection'",[49,203,204],{"class":66},", (",[49,206,208],{"class":207},"s4XuR","socket",[49,210,211],{"class":66},") ",[49,213,214],{"class":62},"=>",[49,216,217],{"class":66}," {\n",[49,219,221,224,227,229,232],{"class":51,"line":220},13,[49,222,223],{"class":66},"  console.",[49,225,226],{"class":130},"log",[49,228,198],{"class":66},[49,230,231],{"class":73},"'一个用户已连接，ID:'",[49,233,234],{"class":66},", socket.id);\n",[49,236,238],{"class":51,"line":237},14,[49,239,114],{"emptyLinePlaceholder":113},[49,241,243],{"class":51,"line":242},15,[49,244,245],{"class":55},"  \u002F\u002F 监听客户端发来的自定义事件 'chat message'\n",[49,247,249,252,254,256,259,261,264,266,268],{"class":51,"line":248},16,[49,250,251],{"class":66},"  socket.",[49,253,195],{"class":130},[49,255,198],{"class":66},[49,257,258],{"class":73},"'chat message'",[49,260,204],{"class":66},[49,262,263],{"class":207},"msg",[49,265,211],{"class":66},[49,267,214],{"class":62},[49,269,217],{"class":66},[49,271,273,276,278,280,283],{"class":51,"line":272},17,[49,274,275],{"class":66},"    console.",[49,277,226],{"class":130},[49,279,198],{"class":66},[49,281,282],{"class":73},"'收到消息:'",[49,284,285],{"class":66},", msg);\n",[49,287,289],{"class":51,"line":288},18,[49,290,291],{"class":55},"    \u002F\u002F 将消息广播给所有已连接的客户端\n",[49,293,295,298,301,303,305],{"class":51,"line":294},19,[49,296,297],{"class":66},"    io.",[49,299,300],{"class":130},"emit",[49,302,198],{"class":66},[49,304,258],{"class":73},[49,306,285],{"class":66},[49,308,310],{"class":51,"line":309},20,[49,311,312],{"class":66},"  });\n",[49,314,316],{"class":51,"line":315},21,[49,317,318],{"class":66},"  \n",[49,320,322],{"class":51,"line":321},22,[49,323,324],{"class":55},"  \u002F\u002F 监听客户端断开连接\n",[49,326,328,330,332,334,337,340,342],{"class":51,"line":327},23,[49,329,251],{"class":66},[49,331,195],{"class":130},[49,333,198],{"class":66},[49,335,336],{"class":73},"'disconnect'",[49,338,339],{"class":66},", () ",[49,341,214],{"class":62},[49,343,217],{"class":66},[49,345,347,349,351,353,356],{"class":51,"line":346},24,[49,348,275],{"class":66},[49,350,226],{"class":130},[49,352,198],{"class":66},[49,354,355],{"class":73},"'用户已断开:'",[49,357,234],{"class":66},[49,359,361],{"class":51,"line":360},25,[49,362,312],{"class":66},[49,364,366],{"class":51,"line":365},26,[49,367,368],{"class":66},"});\n",[49,370,372],{"class":51,"line":371},27,[49,373,114],{"emptyLinePlaceholder":113},[49,375,377,380,383,385,388,390,392],{"class":51,"line":376},28,[49,378,379],{"class":66},"server.",[49,381,382],{"class":130},"listen",[49,384,198],{"class":66},[49,386,387],{"class":123},"3000",[49,389,339],{"class":66},[49,391,214],{"class":62},[49,393,217],{"class":66},[49,395,397,399,401,403,406],{"class":51,"line":396},29,[49,398,223],{"class":66},[49,400,226],{"class":130},[49,402,198],{"class":66},[49,404,405],{"class":73},"'服务端运行在 http:\u002F\u002Flocalhost:3000'",[49,407,408],{"class":66},");\n",[49,410,412],{"class":51,"line":411},30,[49,413,368],{"class":66},[10,415,416],{},"在客户端也就是浏览器上，也需要引入socket.io-client库来创建连接：",[39,418,420],{"className":41,"code":419,"language":43,"meta":44,"style":44},"\u003C!-- 引入客户端库（由服务端自动提供） -->\n\u003Cscript src=\"\u002Fsocket.io\u002Fsocket.io.js\">\u003C\u002Fscript>\n\u003Cscript>\n  \u002F\u002F 连接到服务器，不填 URL 则默认连接当前域名\n  const socket = io();\n\n  socket.on('connect', () => {\n    console.log('已连接到服务器');\n    \u002F\u002F 向服务器发送 'chat message' 事件\n    socket.emit('chat message', '你好，服务端！');\n  });\n\n  \u002F\u002F 监听服务端广播的 'chat message' 事件\n  socket.on('chat message', (msg) => {\n    console.log('收到消息:', msg);\n  });\n\u003C\u002Fscript>\n",[46,421,422,433,459,467,472,486,490,507,520,525,544,548,552,557,577,589,593],{"__ignoreMap":44},[49,423,424,427,430],{"class":51,"line":52},[49,425,426],{"class":62},"\u003C!--",[49,428,429],{"class":66}," 引入客户端库（由服务端自动提供） ",[49,431,432],{"class":62},"-->\n",[49,434,435,438,441,444,447,450,453,456],{"class":51,"line":59},[49,436,437],{"class":62},"\u003C",[49,439,440],{"class":66},"script src",[49,442,443],{"class":62},"=",[49,445,446],{"class":73},"\"\u002Fsocket.io\u002Fsocket.io.js\"",[49,448,449],{"class":62},">",[49,451,452],{"class":66},"\u003C\u002F",[49,454,455],{"class":130},"script",[49,457,458],{"class":66},">\n",[49,460,461,463,465],{"class":51,"line":80},[49,462,437],{"class":66},[49,464,455],{"class":130},[49,466,458],{"class":66},[49,468,469],{"class":51,"line":95},[49,470,471],{"class":55},"  \u002F\u002F 连接到服务器，不填 URL 则默认连接当前域名\n",[49,473,474,477,480,482,484],{"class":51,"line":110},[49,475,476],{"class":62},"  const",[49,478,479],{"class":123}," socket",[49,481,127],{"class":62},[49,483,164],{"class":130},[49,485,134],{"class":66},[49,487,488],{"class":51,"line":117},[49,489,114],{"emptyLinePlaceholder":113},[49,491,492,494,496,498,501,503,505],{"class":51,"line":137},[49,493,251],{"class":66},[49,495,195],{"class":130},[49,497,198],{"class":66},[49,499,500],{"class":73},"'connect'",[49,502,339],{"class":66},[49,504,214],{"class":62},[49,506,217],{"class":66},[49,508,509,511,513,515,518],{"class":51,"line":153},[49,510,275],{"class":66},[49,512,226],{"class":130},[49,514,198],{"class":66},[49,516,517],{"class":73},"'已连接到服务器'",[49,519,408],{"class":66},[49,521,522],{"class":51,"line":159},[49,523,524],{"class":55},"    \u002F\u002F 向服务器发送 'chat message' 事件\n",[49,526,527,530,532,534,536,539,542],{"class":51,"line":178},[49,528,529],{"class":66},"    socket.",[49,531,300],{"class":130},[49,533,198],{"class":66},[49,535,258],{"class":73},[49,537,538],{"class":66},", ",[49,540,541],{"class":73},"'你好，服务端！'",[49,543,408],{"class":66},[49,545,546],{"class":51,"line":183},[49,547,312],{"class":66},[49,549,550],{"class":51,"line":189},[49,551,114],{"emptyLinePlaceholder":113},[49,553,554],{"class":51,"line":220},[49,555,556],{"class":55},"  \u002F\u002F 监听服务端广播的 'chat message' 事件\n",[49,558,559,561,563,565,567,569,571,573,575],{"class":51,"line":237},[49,560,251],{"class":66},[49,562,195],{"class":130},[49,564,198],{"class":66},[49,566,258],{"class":73},[49,568,204],{"class":66},[49,570,263],{"class":207},[49,572,211],{"class":66},[49,574,214],{"class":62},[49,576,217],{"class":66},[49,578,579,581,583,585,587],{"class":51,"line":242},[49,580,275],{"class":66},[49,582,226],{"class":130},[49,584,198],{"class":66},[49,586,282],{"class":73},[49,588,285],{"class":66},[49,590,591],{"class":51,"line":248},[49,592,312],{"class":66},[49,594,595,597,599],{"class":51,"line":272},[49,596,452],{"class":62},[49,598,455],{"class":66},[49,600,458],{"class":62},[10,602,603,604,607,608,611,612,615],{},"在vue\u002Freact等框架中，首先通过 npm 安装 ",[46,605,606],{},"socket.io-client","，然后在组件中引入并使用。关键是在组件的",[21,609,610],{},"生命周期钩子","中建立连接，并在",[21,613,614],{},"组件销毁前断开连接","，以避免内存泄漏。",[39,617,619],{"className":41,"code":618,"language":43,"meta":44,"style":44},"\u002F\u002F Vue 3 示例 (Composition API)\nimport { onMounted, onUnmounted } from 'vue';\nimport { io } from 'socket.io-client';\n\nlet socket;\n\nonMounted(() => {\n  socket = io('http:\u002F\u002Flocalhost:3000');\n  socket.on('connect', () => {\n    console.log('Vue 组件已连接');\n  });\n  socket.on('chat message', (msg) => {\n    console.log('收到消息:', msg);\n  });\n});\n\nonUnmounted(() => {\n  if (socket) {\n    socket.disconnect();\n    console.log('Vue 组件已断开');\n  }\n});\n",[46,620,621,626,640,654,658,666,670,682,698,714,727,731,751,763,767,771,775,786,794,803,816,821],{"__ignoreMap":44},[49,622,623],{"class":51,"line":52},[49,624,625],{"class":55},"\u002F\u002F Vue 3 示例 (Composition API)\n",[49,627,628,630,633,635,638],{"class":51,"line":59},[49,629,63],{"class":62},[49,631,632],{"class":66}," { onMounted, onUnmounted } ",[49,634,70],{"class":62},[49,636,637],{"class":73}," 'vue'",[49,639,77],{"class":66},[49,641,642,644,647,649,652],{"class":51,"line":80},[49,643,63],{"class":62},[49,645,646],{"class":66}," { io } ",[49,648,70],{"class":62},[49,650,651],{"class":73}," 'socket.io-client'",[49,653,77],{"class":66},[49,655,656],{"class":51,"line":95},[49,657,114],{"emptyLinePlaceholder":113},[49,659,660,663],{"class":51,"line":110},[49,661,662],{"class":62},"let",[49,664,665],{"class":66}," socket;\n",[49,667,668],{"class":51,"line":117},[49,669,114],{"emptyLinePlaceholder":113},[49,671,672,675,678,680],{"class":51,"line":137},[49,673,674],{"class":130},"onMounted",[49,676,677],{"class":66},"(() ",[49,679,214],{"class":62},[49,681,217],{"class":66},[49,683,684,687,689,691,693,696],{"class":51,"line":153},[49,685,686],{"class":66},"  socket ",[49,688,443],{"class":62},[49,690,164],{"class":130},[49,692,198],{"class":66},[49,694,695],{"class":73},"'http:\u002F\u002Flocalhost:3000'",[49,697,408],{"class":66},[49,699,700,702,704,706,708,710,712],{"class":51,"line":159},[49,701,251],{"class":66},[49,703,195],{"class":130},[49,705,198],{"class":66},[49,707,500],{"class":73},[49,709,339],{"class":66},[49,711,214],{"class":62},[49,713,217],{"class":66},[49,715,716,718,720,722,725],{"class":51,"line":178},[49,717,275],{"class":66},[49,719,226],{"class":130},[49,721,198],{"class":66},[49,723,724],{"class":73},"'Vue 组件已连接'",[49,726,408],{"class":66},[49,728,729],{"class":51,"line":183},[49,730,312],{"class":66},[49,732,733,735,737,739,741,743,745,747,749],{"class":51,"line":189},[49,734,251],{"class":66},[49,736,195],{"class":130},[49,738,198],{"class":66},[49,740,258],{"class":73},[49,742,204],{"class":66},[49,744,263],{"class":207},[49,746,211],{"class":66},[49,748,214],{"class":62},[49,750,217],{"class":66},[49,752,753,755,757,759,761],{"class":51,"line":220},[49,754,275],{"class":66},[49,756,226],{"class":130},[49,758,198],{"class":66},[49,760,282],{"class":73},[49,762,285],{"class":66},[49,764,765],{"class":51,"line":237},[49,766,312],{"class":66},[49,768,769],{"class":51,"line":242},[49,770,368],{"class":66},[49,772,773],{"class":51,"line":248},[49,774,114],{"emptyLinePlaceholder":113},[49,776,777,780,782,784],{"class":51,"line":272},[49,778,779],{"class":130},"onUnmounted",[49,781,677],{"class":66},[49,783,214],{"class":62},[49,785,217],{"class":66},[49,787,788,791],{"class":51,"line":288},[49,789,790],{"class":62},"  if",[49,792,793],{"class":66}," (socket) {\n",[49,795,796,798,801],{"class":51,"line":294},[49,797,529],{"class":66},[49,799,800],{"class":130},"disconnect",[49,802,134],{"class":66},[49,804,805,807,809,811,814],{"class":51,"line":309},[49,806,275],{"class":66},[49,808,226],{"class":130},[49,810,198],{"class":66},[49,812,813],{"class":73},"'Vue 组件已断开'",[49,815,408],{"class":66},[49,817,818],{"class":51,"line":315},[49,819,820],{"class":66},"  }\n",[49,822,823],{"class":51,"line":321},[49,824,368],{"class":66},[10,826,827,830,831,834,835,838,839,842],{},[13,828,18],{"href":15,"rel":829},[17]," 的强大之处在于其封装的高级特性，核心是",[21,832,833],{},"事件","、",[21,836,837],{},"房间","和",[21,840,841],{},"命名空间","。",[10,844,845,854,857,858,861],{},[21,846,847,848,850,851,853],{},"事件驱动通信 (",[46,849,300],{}," 与 ",[46,852,195],{},")",[855,856],"br",{},"\n这是 ",[13,859,18],{"href":15,"rel":860},[17]," 最基础的通信方式，类似订阅-发布模式。",[863,864,865,876],"ul",{},[866,867,868,871,872,875],"li",{},[46,869,870],{},"socket.emit('eventName', data)","：",[21,873,874],{},"发送","事件。",[866,877,878,871,881,875],{},[46,879,880],{},"socket.on('eventName', callback)",[21,882,883],{},"监听",[10,885,886,889,857,891,894,895,898,899],{},[21,887,888],{},"房间 (Rooms) 与命名空间 (Namespaces)",[855,890],{},[13,892,18],{"href":15,"rel":893},[17]," 用于",[21,896,897],{},"组织和管理连接","的两个核心工具，用于向特定用户群组精确发送消息。",[13,900],{"href":901,"rel":902},"https:\u002F\u002Fsocket.io\u002Fes\u002Fhow-to\u002Femit-to-a-list-of-sockets",[17],[904,905,906,922],"table",{},[907,908,909],"thead",{},[910,911,912,916,919],"tr",{},[913,914,915],"th",{},"特性",[913,917,918],{},"房间 (Room)",[913,920,921],{},"命名空间 (Namespace)",[923,924,925,950,974,997,1019],"tbody",{},[910,926,927,933,940],{},[928,929,930],"td",{},[21,931,932],{},"概念",[928,934,935,936,939],{},"在",[21,937,938],{},"一个命名空间内","的任意分组。",[928,941,935,942,945,946,949],{},[21,943,944],{},"服务器层面","的",[21,947,948],{},"逻辑分离","，相当于不同的通信通道。",[910,951,952,957,964],{},[928,953,954],{},[21,955,956],{},"创建方式",[928,958,959,960,963],{},"动态创建，客户端或服务端均可让 socket 加入\u002F离开（",[46,961,962],{},"socket.join('roomId')","）。",[928,965,966,967,970,971,963],{},"在服务端通过 ",[46,968,969],{},"io.of('\u002Fnamespace')"," 显式创建，客户端连接时指定（",[46,972,973],{},"io('\u002Fnamespace')",[910,975,976,981,984],{},[928,977,978],{},[21,979,980],{},"典型场景",[928,982,983],{},"聊天室成员、游戏房间、文档的协作者。",[928,985,986,987,834,990,834,993,996],{},"将应用分隔为 ",[46,988,989],{},"\u002Fadmin",[46,991,992],{},"\u002Fchat",[46,994,995],{},"\u002Fnotifications"," 等模块。",[910,998,999,1004,1013],{},[928,1000,1001],{},[21,1002,1003],{},"核心优势",[928,1005,1006,1009,1010],{},[21,1007,1008],{},"动态、灵活","，非常适合基于业务逻辑（如用户ID、项目ID）进行分组广播。",[13,1011],{"href":901,"rel":1012},[17],[928,1014,1015,1018],{},[21,1016,1017],{},"严格的隔离","，不同命名空间之间的连接和事件完全独立，可分别设置中间件。",[910,1020,1021,1036,1038],{},[928,1022,1023,1024,1027,1028,1031,1032,1035],{},".NET 生态中有成熟的 ",[13,1025,18],{"href":15,"rel":1026},[17]," 解决方案，虽然官方（",[13,1029,18],{"href":15,"rel":1030},[17]," 团队）并未提供原生支持，但社区贡献了许多高质量的 NuGet 包，涵盖了客户端和服务端的实现，在服务端或桌面端（如 WPF、控制台）连接 ",[13,1033,18],{"href":15,"rel":1034},[17]," 服务时，主要有以下几个选择：",[928,1037],{},[928,1039],{},[10,1041,1042,1047,1048,1051,1052,1055],{},[21,1043,1044],{},[46,1045,1046],{},"SocketIOClient"," 是目前为止下载量最高的，功能最全面的，支持 ",[46,1049,1050],{},"EmitAsync","、Ack 回调、二进制传输、自定义序列化（默认 ",[46,1053,1054],{},"System.Text.Json","）等。适合需要高兼容性、功能完备的通用场景。",[10,1057,1058,1063],{},[21,1059,1060],{},[46,1061,1062],{},"H.Socket.IO"," 主打高性能和轻量级，API 设计简洁，支持命名空间（Namespaces），完全异步。适合追求极致性能、代码简洁，且不需要复杂 Ack 机制的轻量级应用。",[10,1065,1066,1067,1074],{},".NET 客户端库的主要用途是",[21,1068,1069,1070,1073],{},"连接官方（Node.js）的 ",[13,1071,18],{"href":15,"rel":1072},[17]," 服务端","。虽然大多数库在协议层面兼容，但版本迭代时仍可能遇到细微的兼容性问题。",[1076,1077,1078],"blockquote",{},[10,1079,1080,1081,1084,1085,1088,1089,1091,1092,1094,1095,1098,1099,1104],{},"最典型的例子是：如果你的服务端使用 ",[13,1082,18],{"href":15,"rel":1083},[17]," ",[21,1086,1087],{},"v4","，应优先选择明确声明支持该版本的客户端库（如 ",[46,1090,1046],{}," 或 ",[46,1093,1062],{},"），并留意其文档中对 ",[46,1096,1097],{},"EIO"," 协议版本（",[13,1100,1103],{"href":1101,"rel":1102},"https:\u002F\u002Fengine.io\u002F",[17],"Engine.IO"," V3\u002FV4）的配置说明。",[10,1106,1107,1108,1111,1112,1118,1119,1122,1123,1126,1127,1134],{},"对于ASP.NET开发者来说，有相应的服务端使用的SOCKETIO库，比如说",[21,1109,1110],{},"SocketIOSharp2"," ，基于 ",[21,1113,1114,1117],{},[13,1115,18],{"href":15,"rel":1116},[17]," 协议 v4"," (",[13,1120,1103],{"href":1101,"rel":1121},[17]," v4)，是对原 ",[46,1124,1125],{},"SocketIOSharp"," 的活跃分支。",[21,1128,1129,1130,1133],{},"关键优势是支持连接预处理（",[46,1131,1132],{},"OnConnecting","）","，可在握手阶段进行Token验证，非常适合需要鉴权的生产环境。",[10,1136,1137,1140,1141,1144],{},[21,1138,1139],{},"CORE.HELPER"," 提供 ",[46,1142,1143],{},"SocketIOServer"," 类，API 设计简洁，支持连接、事件订阅等基本功能，适合快速原型验证或简单场景。",[10,1146,1147,1148,1150,1151,1156,1157,1160,1161,1164],{},"以 ",[46,1149,1110],{}," 为例，演示如何将其嵌入 ",[13,1152,1155],{"href":1153,"rel":1154},"https:\u002F\u002Fasp.net\u002F",[17],"ASP.NET"," Core 应用。关键是将 ",[13,1158,18],{"href":15,"rel":1159},[17]," 服务作为一个 ",[21,1162,1163],{},"Hosted Service（后台服务）"," 启动。",[10,1166,1167],{},"需要先创建socket.io的后台服务：",[39,1169,1173],{"className":1170,"code":1171,"language":1172,"meta":44,"style":44},"language-cs shiki shiki-themes github-light github-dark","using SocketIOSharp.Server;\nusing SocketIOSharp.Server.Client;\nusing SocketIOSharp.Common;\n\npublic class SocketIoServerService : BackgroundService\n{\n    private readonly ILogger\u003CSocketIoServerService> _logger;\n    private SocketIOServer? _server;\n\n    public SocketIoServerService(ILogger\u003CSocketIoServerService> logger)\n    {\n        _logger = logger;\n    }\n\n    protected override Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        \u002F\u002F 1. 配置监听端口（可与 ASP.NET Core 的 HTTP 端口不同，这里用 9092）\n        var option = new SocketIOServerOption(port: 9092);\n        _server = new SocketIOServer(option);\n\n        \u002F\u002F 2. 连接预处理（可选，用于鉴权）\n        _server.OnConnecting(context =>\n        {\n            \u002F\u002F context.Item1 包含查询参数，可从中提取 token\n            var token = context.Item1[\"token\"];\n            bool isValid = !string.IsNullOrWhiteSpace(token);\n            _server.AddVerificationResult(context.Item2, isValid);\n        });\n\n        \u002F\u002F 3. 监听客户端连接\n        _server.OnConnection(socket =>\n        {\n            _logger.LogInformation(\"Client connected: {SocketId}\", socket.Id);\n\n            \u002F\u002F 监听客户端发来的 'chat message' 事件\n            socket.On(\"chat message\", data =>\n            {\n                _logger.LogInformation(\"Received message: {Data}\", data?.ToString());\n                \n                \u002F\u002F 向所有客户端广播（这里简化处理，实际应遍历所有连接的 socket）\n                socket.Emit(\"chat message\", $\"Echo: {data}\");\n            });\n\n            \u002F\u002F 监听断开连接事件\n            socket.On(SocketIOEvent.DISCONNECT, () =>\n            {\n                _logger.LogInformation(\"Client disconnected: {SocketId}\", socket.Id);\n            });\n        });\n\n        \u002F\u002F 4. 启动 Socket.IO 服务\n        _server.Start();\n        _logger.LogInformation(\"Socket.IO server started on port 9092\");\n\n        \u002F\u002F 保持服务运行，直到应用关闭\n        stoppingToken.WaitHandle.WaitOne();\n\n        \u002F\u002F 5. 关闭时清理资源\n        _server.Stop();\n        _server.Dispose();\n        return Task.CompletedTask;\n    }\n}\n","cs",[46,1174,1175,1191,1208,1221,1225,1242,1247,1271,1286,1290,1314,1319,1329,1334,1338,1362,1366,1371,1399,1413,1417,1422,1437,1442,1447,1466,1490,1501,1506,1510,1515,1529,1534,1551,1556,1562,1583,1589,1616,1622,1628,1659,1665,1670,1676,1689,1694,1708,1713,1718,1723,1729,1739,1754,1759,1765,1776,1781,1787,1797,1807,1816,1821],{"__ignoreMap":44},[49,1176,1177,1180,1183,1186,1189],{"class":51,"line":52},[49,1178,1179],{"class":62},"using",[49,1181,1182],{"class":130}," SocketIOSharp",[49,1184,1185],{"class":66},".",[49,1187,1188],{"class":130},"Server",[49,1190,77],{"class":66},[49,1192,1193,1195,1197,1199,1201,1203,1206],{"class":51,"line":59},[49,1194,1179],{"class":62},[49,1196,1182],{"class":130},[49,1198,1185],{"class":66},[49,1200,1188],{"class":130},[49,1202,1185],{"class":66},[49,1204,1205],{"class":130},"Client",[49,1207,77],{"class":66},[49,1209,1210,1212,1214,1216,1219],{"class":51,"line":80},[49,1211,1179],{"class":62},[49,1213,1182],{"class":130},[49,1215,1185],{"class":66},[49,1217,1218],{"class":130},"Common",[49,1220,77],{"class":66},[49,1222,1223],{"class":51,"line":95},[49,1224,114],{"emptyLinePlaceholder":113},[49,1226,1227,1230,1233,1236,1239],{"class":51,"line":110},[49,1228,1229],{"class":62},"public",[49,1231,1232],{"class":62}," class",[49,1234,1235],{"class":130}," SocketIoServerService",[49,1237,1238],{"class":66}," : ",[49,1240,1241],{"class":130},"BackgroundService\n",[49,1243,1244],{"class":51,"line":117},[49,1245,1246],{"class":66},"{\n",[49,1248,1249,1252,1255,1258,1260,1263,1266,1269],{"class":51,"line":137},[49,1250,1251],{"class":62},"    private",[49,1253,1254],{"class":62}," readonly",[49,1256,1257],{"class":130}," ILogger",[49,1259,437],{"class":66},[49,1261,1262],{"class":130},"SocketIoServerService",[49,1264,1265],{"class":66},"> ",[49,1267,1268],{"class":130},"_logger",[49,1270,77],{"class":66},[49,1272,1273,1275,1278,1281,1284],{"class":51,"line":153},[49,1274,1251],{"class":62},[49,1276,1277],{"class":130}," SocketIOServer",[49,1279,1280],{"class":66},"? ",[49,1282,1283],{"class":130},"_server",[49,1285,77],{"class":66},[49,1287,1288],{"class":51,"line":159},[49,1289,114],{"emptyLinePlaceholder":113},[49,1291,1292,1295,1297,1299,1302,1304,1306,1308,1311],{"class":51,"line":178},[49,1293,1294],{"class":62},"    public",[49,1296,1235],{"class":130},[49,1298,198],{"class":66},[49,1300,1301],{"class":130},"ILogger",[49,1303,437],{"class":66},[49,1305,1262],{"class":130},[49,1307,1265],{"class":66},[49,1309,1310],{"class":130},"logger",[49,1312,1313],{"class":66},")\n",[49,1315,1316],{"class":51,"line":183},[49,1317,1318],{"class":66},"    {\n",[49,1320,1321,1324,1326],{"class":51,"line":189},[49,1322,1323],{"class":66},"        _logger ",[49,1325,443],{"class":62},[49,1327,1328],{"class":66}," logger;\n",[49,1330,1331],{"class":51,"line":220},[49,1332,1333],{"class":66},"    }\n",[49,1335,1336],{"class":51,"line":237},[49,1337,114],{"emptyLinePlaceholder":113},[49,1339,1340,1343,1346,1349,1352,1354,1357,1360],{"class":51,"line":242},[49,1341,1342],{"class":62},"    protected",[49,1344,1345],{"class":62}," override",[49,1347,1348],{"class":130}," Task",[49,1350,1351],{"class":130}," ExecuteAsync",[49,1353,198],{"class":66},[49,1355,1356],{"class":130},"CancellationToken",[49,1358,1359],{"class":130}," stoppingToken",[49,1361,1313],{"class":66},[49,1363,1364],{"class":51,"line":248},[49,1365,1318],{"class":66},[49,1367,1368],{"class":51,"line":272},[49,1369,1370],{"class":55},"        \u002F\u002F 1. 配置监听端口（可与 ASP.NET Core 的 HTTP 端口不同，这里用 9092）\n",[49,1372,1373,1376,1379,1381,1383,1386,1388,1391,1394,1397],{"class":51,"line":288},[49,1374,1375],{"class":62},"        var",[49,1377,1378],{"class":130}," option",[49,1380,127],{"class":62},[49,1382,169],{"class":62},[49,1384,1385],{"class":130}," SocketIOServerOption",[49,1387,198],{"class":66},[49,1389,1390],{"class":130},"port",[49,1392,1393],{"class":66},": ",[49,1395,1396],{"class":123},"9092",[49,1398,408],{"class":66},[49,1400,1401,1404,1406,1408,1410],{"class":51,"line":294},[49,1402,1403],{"class":66},"        _server ",[49,1405,443],{"class":62},[49,1407,169],{"class":62},[49,1409,1277],{"class":130},[49,1411,1412],{"class":66},"(option);\n",[49,1414,1415],{"class":51,"line":309},[49,1416,114],{"emptyLinePlaceholder":113},[49,1418,1419],{"class":51,"line":315},[49,1420,1421],{"class":55},"        \u002F\u002F 2. 连接预处理（可选，用于鉴权）\n",[49,1423,1424,1427,1429,1431,1434],{"class":51,"line":321},[49,1425,1426],{"class":66},"        _server.",[49,1428,1132],{"class":130},[49,1430,198],{"class":66},[49,1432,1433],{"class":130},"context",[49,1435,1436],{"class":62}," =>\n",[49,1438,1439],{"class":51,"line":327},[49,1440,1441],{"class":66},"        {\n",[49,1443,1444],{"class":51,"line":346},[49,1445,1446],{"class":55},"            \u002F\u002F context.Item1 包含查询参数，可从中提取 token\n",[49,1448,1449,1452,1455,1457,1460,1463],{"class":51,"line":360},[49,1450,1451],{"class":62},"            var",[49,1453,1454],{"class":130}," token",[49,1456,127],{"class":62},[49,1458,1459],{"class":66}," context.Item1[",[49,1461,1462],{"class":73},"\"token\"",[49,1464,1465],{"class":66},"];\n",[49,1467,1468,1471,1474,1476,1479,1482,1484,1487],{"class":51,"line":365},[49,1469,1470],{"class":62},"            bool",[49,1472,1473],{"class":130}," isValid",[49,1475,127],{"class":62},[49,1477,1478],{"class":62}," !",[49,1480,1481],{"class":62},"string",[49,1483,1185],{"class":66},[49,1485,1486],{"class":130},"IsNullOrWhiteSpace",[49,1488,1489],{"class":66},"(token);\n",[49,1491,1492,1495,1498],{"class":51,"line":371},[49,1493,1494],{"class":66},"            _server.",[49,1496,1497],{"class":130},"AddVerificationResult",[49,1499,1500],{"class":66},"(context.Item2, isValid);\n",[49,1502,1503],{"class":51,"line":376},[49,1504,1505],{"class":66},"        });\n",[49,1507,1508],{"class":51,"line":396},[49,1509,114],{"emptyLinePlaceholder":113},[49,1511,1512],{"class":51,"line":411},[49,1513,1514],{"class":55},"        \u002F\u002F 3. 监听客户端连接\n",[49,1516,1518,1520,1523,1525,1527],{"class":51,"line":1517},31,[49,1519,1426],{"class":66},[49,1521,1522],{"class":130},"OnConnection",[49,1524,198],{"class":66},[49,1526,208],{"class":130},[49,1528,1436],{"class":62},[49,1530,1532],{"class":51,"line":1531},32,[49,1533,1441],{"class":66},[49,1535,1537,1540,1543,1545,1548],{"class":51,"line":1536},33,[49,1538,1539],{"class":66},"            _logger.",[49,1541,1542],{"class":130},"LogInformation",[49,1544,198],{"class":66},[49,1546,1547],{"class":73},"\"Client connected: {SocketId}\"",[49,1549,1550],{"class":66},", socket.Id);\n",[49,1552,1554],{"class":51,"line":1553},34,[49,1555,114],{"emptyLinePlaceholder":113},[49,1557,1559],{"class":51,"line":1558},35,[49,1560,1561],{"class":55},"            \u002F\u002F 监听客户端发来的 'chat message' 事件\n",[49,1563,1565,1568,1571,1573,1576,1578,1581],{"class":51,"line":1564},36,[49,1566,1567],{"class":66},"            socket.",[49,1569,1570],{"class":130},"On",[49,1572,198],{"class":66},[49,1574,1575],{"class":73},"\"chat message\"",[49,1577,538],{"class":66},[49,1579,1580],{"class":130},"data",[49,1582,1436],{"class":62},[49,1584,1586],{"class":51,"line":1585},37,[49,1587,1588],{"class":66},"            {\n",[49,1590,1592,1595,1597,1599,1602,1605,1608,1610,1613],{"class":51,"line":1591},38,[49,1593,1594],{"class":66},"                _logger.",[49,1596,1542],{"class":130},[49,1598,198],{"class":66},[49,1600,1601],{"class":73},"\"Received message: {Data}\"",[49,1603,1604],{"class":66},", data",[49,1606,1607],{"class":62},"?",[49,1609,1185],{"class":66},[49,1611,1612],{"class":130},"ToString",[49,1614,1615],{"class":66},"());\n",[49,1617,1619],{"class":51,"line":1618},39,[49,1620,1621],{"class":66},"                \n",[49,1623,1625],{"class":51,"line":1624},40,[49,1626,1627],{"class":55},"                \u002F\u002F 向所有客户端广播（这里简化处理，实际应遍历所有连接的 socket）\n",[49,1629,1631,1634,1637,1639,1641,1643,1646,1649,1651,1654,1657],{"class":51,"line":1630},41,[49,1632,1633],{"class":66},"                socket.",[49,1635,1636],{"class":130},"Emit",[49,1638,198],{"class":66},[49,1640,1575],{"class":73},[49,1642,538],{"class":66},[49,1644,1645],{"class":73},"$\"Echo: ",[49,1647,1648],{"class":73},"{",[49,1650,1580],{"class":66},[49,1652,1653],{"class":73},"}",[49,1655,1656],{"class":73},"\"",[49,1658,408],{"class":66},[49,1660,1662],{"class":51,"line":1661},42,[49,1663,1664],{"class":66},"            });\n",[49,1666,1668],{"class":51,"line":1667},43,[49,1669,114],{"emptyLinePlaceholder":113},[49,1671,1673],{"class":51,"line":1672},44,[49,1674,1675],{"class":55},"            \u002F\u002F 监听断开连接事件\n",[49,1677,1679,1681,1683,1686],{"class":51,"line":1678},45,[49,1680,1567],{"class":66},[49,1682,1570],{"class":130},[49,1684,1685],{"class":66},"(SocketIOEvent.DISCONNECT, () ",[49,1687,1688],{"class":62},"=>\n",[49,1690,1692],{"class":51,"line":1691},46,[49,1693,1588],{"class":66},[49,1695,1697,1699,1701,1703,1706],{"class":51,"line":1696},47,[49,1698,1594],{"class":66},[49,1700,1542],{"class":130},[49,1702,198],{"class":66},[49,1704,1705],{"class":73},"\"Client disconnected: {SocketId}\"",[49,1707,1550],{"class":66},[49,1709,1711],{"class":51,"line":1710},48,[49,1712,1664],{"class":66},[49,1714,1716],{"class":51,"line":1715},49,[49,1717,1505],{"class":66},[49,1719,1721],{"class":51,"line":1720},50,[49,1722,114],{"emptyLinePlaceholder":113},[49,1724,1726],{"class":51,"line":1725},51,[49,1727,1728],{"class":55},"        \u002F\u002F 4. 启动 Socket.IO 服务\n",[49,1730,1732,1734,1737],{"class":51,"line":1731},52,[49,1733,1426],{"class":66},[49,1735,1736],{"class":130},"Start",[49,1738,134],{"class":66},[49,1740,1742,1745,1747,1749,1752],{"class":51,"line":1741},53,[49,1743,1744],{"class":66},"        _logger.",[49,1746,1542],{"class":130},[49,1748,198],{"class":66},[49,1750,1751],{"class":73},"\"Socket.IO server started on port 9092\"",[49,1753,408],{"class":66},[49,1755,1757],{"class":51,"line":1756},54,[49,1758,114],{"emptyLinePlaceholder":113},[49,1760,1762],{"class":51,"line":1761},55,[49,1763,1764],{"class":55},"        \u002F\u002F 保持服务运行，直到应用关闭\n",[49,1766,1768,1771,1774],{"class":51,"line":1767},56,[49,1769,1770],{"class":66},"        stoppingToken.WaitHandle.",[49,1772,1773],{"class":130},"WaitOne",[49,1775,134],{"class":66},[49,1777,1779],{"class":51,"line":1778},57,[49,1780,114],{"emptyLinePlaceholder":113},[49,1782,1784],{"class":51,"line":1783},58,[49,1785,1786],{"class":55},"        \u002F\u002F 5. 关闭时清理资源\n",[49,1788,1790,1792,1795],{"class":51,"line":1789},59,[49,1791,1426],{"class":66},[49,1793,1794],{"class":130},"Stop",[49,1796,134],{"class":66},[49,1798,1800,1802,1805],{"class":51,"line":1799},60,[49,1801,1426],{"class":66},[49,1803,1804],{"class":130},"Dispose",[49,1806,134],{"class":66},[49,1808,1810,1813],{"class":51,"line":1809},61,[49,1811,1812],{"class":62},"        return",[49,1814,1815],{"class":66}," Task.CompletedTask;\n",[49,1817,1819],{"class":51,"line":1818},62,[49,1820,1333],{"class":66},[49,1822,1824],{"class":51,"line":1823},63,[49,1825,1826],{"class":66},"}\n",[10,1828,1829],{},"然后在Program.cs中注册和启动服务：",[39,1831,1833],{"className":1170,"code":1832,"language":1172,"meta":44,"style":44},"var builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F 注册 Socket.IO 后台服务\nbuilder.Services.AddHostedService\u003CSocketIoServerService>();\n\nvar app = builder.Build();\n\n\u002F\u002F 你的其他 ASP.NET Core 中间件（如 MapControllers 等）...\n\napp.Run();\n",[46,1834,1835,1854,1858,1863,1878,1882,1898,1902,1907,1911],{"__ignoreMap":44},[49,1836,1837,1840,1843,1845,1848,1851],{"class":51,"line":52},[49,1838,1839],{"class":62},"var",[49,1841,1842],{"class":130}," builder",[49,1844,127],{"class":62},[49,1846,1847],{"class":66}," WebApplication.",[49,1849,1850],{"class":130},"CreateBuilder",[49,1852,1853],{"class":66},"(args);\n",[49,1855,1856],{"class":51,"line":59},[49,1857,114],{"emptyLinePlaceholder":113},[49,1859,1860],{"class":51,"line":80},[49,1861,1862],{"class":55},"\u002F\u002F 注册 Socket.IO 后台服务\n",[49,1864,1865,1868,1871,1873,1875],{"class":51,"line":95},[49,1866,1867],{"class":66},"builder.Services.",[49,1869,1870],{"class":130},"AddHostedService",[49,1872,437],{"class":66},[49,1874,1262],{"class":130},[49,1876,1877],{"class":66},">();\n",[49,1879,1880],{"class":51,"line":110},[49,1881,114],{"emptyLinePlaceholder":113},[49,1883,1884,1886,1888,1890,1893,1896],{"class":51,"line":117},[49,1885,1839],{"class":62},[49,1887,124],{"class":130},[49,1889,127],{"class":62},[49,1891,1892],{"class":66}," builder.",[49,1894,1895],{"class":130},"Build",[49,1897,134],{"class":66},[49,1899,1900],{"class":51,"line":137},[49,1901,114],{"emptyLinePlaceholder":113},[49,1903,1904],{"class":51,"line":153},[49,1905,1906],{"class":55},"\u002F\u002F 你的其他 ASP.NET Core 中间件（如 MapControllers 等）...\n",[49,1908,1909],{"class":51,"line":159},[49,1910,114],{"emptyLinePlaceholder":113},[49,1912,1913,1916,1919],{"class":51,"line":178},[49,1914,1915],{"class":66},"app.",[49,1917,1918],{"class":130},"Run",[49,1920,134],{"class":66},[10,1922,1923],{},"前端就使用Socket.IO客户端库，需要连接到上一步配置的端口：",[39,1925,1927],{"className":41,"code":1926,"language":43,"meta":44,"style":44},"\u002F\u002F 假设 Socket.IO 服务运行在 localhost:9092\nconst socket = io('http:\u002F\u002Flocalhost:9092', {\n    query: { token: 'your_auth_token' } \u002F\u002F 若配置了 OnConnecting 鉴权\n});\n\nsocket.on('connect', () => {\n    console.log('Connected to Socket.IO server');\n    socket.emit('chat message', 'Hello from Web Client!');\n});\n\nsocket.on('chat message', (msg) => {\n    console.log('Received:', msg);\n});\n",[46,1928,1929,1934,1952,1966,1970,1974,1991,2004,2021,2025,2029,2049,2062],{"__ignoreMap":44},[49,1930,1931],{"class":51,"line":52},[49,1932,1933],{"class":55},"\u002F\u002F 假设 Socket.IO 服务运行在 localhost:9092\n",[49,1935,1936,1938,1940,1942,1944,1946,1949],{"class":51,"line":59},[49,1937,120],{"class":62},[49,1939,479],{"class":123},[49,1941,127],{"class":62},[49,1943,164],{"class":130},[49,1945,198],{"class":66},[49,1947,1948],{"class":73},"'http:\u002F\u002Flocalhost:9092'",[49,1950,1951],{"class":66},", {\n",[49,1953,1954,1957,1960,1963],{"class":51,"line":80},[49,1955,1956],{"class":66},"    query: { token: ",[49,1958,1959],{"class":73},"'your_auth_token'",[49,1961,1962],{"class":66}," } ",[49,1964,1965],{"class":55},"\u002F\u002F 若配置了 OnConnecting 鉴权\n",[49,1967,1968],{"class":51,"line":95},[49,1969,368],{"class":66},[49,1971,1972],{"class":51,"line":110},[49,1973,114],{"emptyLinePlaceholder":113},[49,1975,1976,1979,1981,1983,1985,1987,1989],{"class":51,"line":117},[49,1977,1978],{"class":66},"socket.",[49,1980,195],{"class":130},[49,1982,198],{"class":66},[49,1984,500],{"class":73},[49,1986,339],{"class":66},[49,1988,214],{"class":62},[49,1990,217],{"class":66},[49,1992,1993,1995,1997,1999,2002],{"class":51,"line":137},[49,1994,275],{"class":66},[49,1996,226],{"class":130},[49,1998,198],{"class":66},[49,2000,2001],{"class":73},"'Connected to Socket.IO server'",[49,2003,408],{"class":66},[49,2005,2006,2008,2010,2012,2014,2016,2019],{"class":51,"line":153},[49,2007,529],{"class":66},[49,2009,300],{"class":130},[49,2011,198],{"class":66},[49,2013,258],{"class":73},[49,2015,538],{"class":66},[49,2017,2018],{"class":73},"'Hello from Web Client!'",[49,2020,408],{"class":66},[49,2022,2023],{"class":51,"line":159},[49,2024,368],{"class":66},[49,2026,2027],{"class":51,"line":178},[49,2028,114],{"emptyLinePlaceholder":113},[49,2030,2031,2033,2035,2037,2039,2041,2043,2045,2047],{"class":51,"line":183},[49,2032,1978],{"class":66},[49,2034,195],{"class":130},[49,2036,198],{"class":66},[49,2038,258],{"class":73},[49,2040,204],{"class":66},[49,2042,263],{"class":207},[49,2044,211],{"class":66},[49,2046,214],{"class":62},[49,2048,217],{"class":66},[49,2050,2051,2053,2055,2057,2060],{"class":51,"line":189},[49,2052,275],{"class":66},[49,2054,226],{"class":130},[49,2056,198],{"class":66},[49,2058,2059],{"class":73},"'Received:'",[49,2061,285],{"class":66},[49,2063,2064],{"class":51,"line":220},[49,2065,368],{"class":66},[10,2067,2068,2071,2072,2075,2076,2078,2079,2082,2083,2086,2087,2090,2091,2094,2095,2101,2102,2104,2105,2108,2109,2116,2117,2123,2124,2127],{},[13,2069,18],{"href":15,"rel":2070},[17]," 服务端需要",[21,2073,2074],{},"独立监听一个端口","（如上述 ",[46,2077,1396],{},"），它与 ",[13,2080,1155],{"href":1153,"rel":2081},[17]," Core 的 HTTP\u002FKestrel 管道是",[21,2084,2085],{},"并行运行","的。你无法直接通过 ",[13,2088,1155],{"href":1153,"rel":2089},[17]," Core 的 ",[46,2092,2093],{},"\u002Fsocket.io"," 路径来访问它。务必确认你选择的库所支持的 ",[21,2096,2097,2100],{},[13,2098,1103],{"href":1101,"rel":2099},[17]," 协议版本","（",[46,2103,1110],{}," 支持 v4）与前端 ",[13,2106,18],{"href":15,"rel":2107},[17]," 客户端版本匹配，这是连接成功的关键。如果你没有",[21,2110,2111,2112,2115],{},"必须使用 ",[13,2113,18],{"href":15,"rel":2114},[17]," 客户端","的硬性要求，在纯 .NET 技术栈中，官方推荐的 ",[21,2118,2119,2122],{},[13,2120,1155],{"href":1153,"rel":2121},[17]," Core SignalR"," 是更原生的选择，它深度集成于 ",[13,2125,1155],{"href":1153,"rel":2126},[17]," Core 管道，无需额外端口和第三方库。",[2129,2130,2131],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":44,"searchDepth":59,"depth":59,"links":2133},[],"Socket.IO 是一个基于事件驱动的实时双向通信库，它的核心价值在于封装了 WebSocket 的复杂性，并提供了自动降级、自动重连、房间（Room）分组等开箱即用的高级功能。","md",{"date":2137,"tags":2138},"2026-10-09",[2139,2140,2141],"network","socketio","nodejs","\u002Fnetwork\u002F探究socketio事件驱动的双向通信库",{"description":2134},"network\u002F探究SocketIO事件驱动的双向通信库","5Ppnr_1HEpzY5Kv9z8yBuU5fyH_MADICcIYGBs2XulM",1793064680748]