socket多人聊天简易实现

先瞅瞅极其简陋的界面



服务端实现
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
const http = require('http');
const WebSocket = require('ws');
const url = require('url');
var fs = require('fs');
var path = require('path');
var mime = require('mime');

var cache = {};
const wssObj = {};

const server = http.createServer(function (request, response) {
var filePath = false;
if (request.url == '/') {
filePath = 'public/hws.html';
} else {
filePath = 'public' + request.url;
}
var absPath = './' + filePath;
serveStatic(response, cache, absPath);
});

// 将未缓存的静态文件缓存到内存中
function serveStatic(response, cache, absPath) {
if (cache[absPath]) {
sendFile(response, absPath, cache[absPath]);
} else {
fs.exists(absPath, function (exists) {// 检查文件是否存在
if (exists) {
fs.readFile(absPath, function (err, data) {
if (err) {
send404(response);
} else {
cache[absPath] = data;
sendFile(response, absPath, data);
}
});
} else {
send404(response);
}
});
}
}

// 提供文件数据服务,识别响应头,发送文件内容
function sendFile(response, filePath, fileContents) {
response.writeHead(200, {
"content-type": mime.lookup(path.basename(filePath))
});
response.end(fileContents);
}

// 所请求的文件不存在时发送404错误
function send404(response) {
response.writeHead(404, { 'Content-Type': 'text/plain' });
response.write('Error 404:resource not found');
response.end();
}

// 广播
const broadcast = (wss, ws, data) => {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
};

// 广播,包括自己
const broadcastSelf = () => {
let list = [];
for (let i in wssObj) {
list.push(i);
}
let data = {
event: 'roomlist',
data: list
};
for (let i in wssObj) {
wssObj[i].clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
}
};

// 新建一个 socket
const createRoom = () => {
const wss = new WebSocket.Server({ noServer: true });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
broadcast(wss, ws, {
event: 'message',
data: data,
who: ws.nickname
});
});
ws.on('close', () => {
broadcast(wss, ws, {
event: 'close',
data: '离开房间',
who: ws.nickname
});
});
broadcastSelf();
});
return wss;
}

// 触发连接
const join = (data, request, socket, head) => {
const wss = wssObj[data.roomname];
if (wss) {
wss.handleUpgrade(request, socket, head, function done(ws) {
ws.nickname = data.nickname;
wss.emit('connection', ws, request);
});
} else {
wssObj[data.roomname] = createRoom();
join(data, request, socket, head);
}
};

// 接请求
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
const query = url.parse(request.url).query;
const queryData = {};
query.split('&').forEach(kv => {
let z = kv.split('=');
queryData[decodeURIComponent(z[0])] = decodeURIComponent(z[1]);
});

if (pathname === '/join') {
join(queryData, request, socket, head);
} else {
socket.destroy();
}
});

server.listen(10002);
页面实现
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>

<style>
#join,#leave {
width: 66px;
height: 36px;
border-radius: 5px;
background-color: cornflowerblue;
text-align: center;
line-height: 36px;
cursor: pointer;
display: inline-block;
}
#panel {
width: 400px;
height: 300px;
border: 1px solid #aaa;
overflow: auto;
}

.self-p{
text-align: right;
}
</style>

</head>

<body>
<div>
<span>room</span><input type="text" id="room">
<span>nickname</span><input type="text" id="nickname">
<input type="button" id="join" value="join">
<input type="button" id="leave" value="leave">
</div>

<div id="panel">

</div>

<div>
<span>输入</span><input type="text" id="form">
</div>

<div>
<p>roomlist</p>
<p id="roomlist"></p>
</div>
</body>

<script>

let ws = null;
join.addEventListener('click', event => {
let roomname = document.getElementById('room').value;
let nickname = document.getElementById('nickname').value;
ws = new WebSocket(`ws://127.0.0.1:10002/join?roomname=${roomname}&nickname=${nickname}`);
ws.addEventListener('open', function open() {
ws.send(` ${nickname} 加入房间`);
});
ws.addEventListener('message', function incoming(message) {
let msg = JSON.parse(message.data);
if (msg.event === 'roomlist') {
roomlist.innerHTML = msg.data.join(',');
} else {
panel.innerHTML += (`<p>${msg.who}: ${msg.data}</p>`);
}
});
join.setAttribute('disabled', true);
});

leave.addEventListener('click', () => {
ws.close();
join.removeAttribute('disabled');
});

form.addEventListener('change', event => {
ws && ws.send(event.target.value);
panel.innerHTML += (`<p class="self-p">${event.target.value} :${nickname.value}</p>`);
event.target.value = '';
});
</script>

</html>