使用socket实现即时通讯聊天室

websocket早在几年前就已经很流行了,主要就是用于即时通讯这一方面应用,可以是聊天,也可使是直播流传输等等。

今天,就来说说如何使用 create-react-app + socket.io 实现简单的即时聊天。

Demo地址

准备工作

想要实现即时通讯,还是需要有服务器的支持,这里我使用的是一个简单配置的服务器

服务器配置

还是去年腾讯搞活动买的,还不错,有机会你们也可以去看看。阿里云腾讯云都会时不时的出一些活动,买一个服务器自己玩玩还是可以的。如果有活动,我可以在后面不断更新。

有了服务器以后就是敲代码了。

大爷,咋又是你啊

服务端实现

服务端我这里使用的是Nodejs作为后端语言,使用express+socket.io作为技术支持,具体的代码如下

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
const express = require("express")
const app = express()
const http = require("http").createServer(app)
var io = require('socket.io')(http);

app.use(express.static(__dirname + '/dist'))
app.get("/", (req, res) => {
res.header("Access-Control-Allow-Credentials", "true");
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
// res.send(`<h1>Hello World!!!</h1>`)
res.send(__dirname + '/dist/index.html')
})

const userList = {};
let userCount = 0;
const messageList = []

io.on("connection", socket => {

socket.on("login", data => {
console.log(`${data.username} 登录`);
socket.uid = data.uid
userList[data.uid] = data.username
userCount++

io.emit('users', {
userCount,
userList
})

// 发给自己
socket.emit("receive_message", messageList)
})

socket.on("disconnect", function() {
if( !socket.uid ) return
const user = {
uid: socket.uid,
username: userList[socket.uid]
}
delete userList[socket.uid]
userCount--
// 发送给所有用户 使用 io.emit
// 发送给自己 使用 socket.emit
io.emit('users', {
userCount,
userList
})
console.log(`${user.username} 登出了`);
})


socket.on("message", data => {
if ( !data ) return
// console.log(`发送信息 -- ${data}`);
messageList.push({
username: userList[socket.uid],
message: data
})
if( messageList.length > 30 ){
messageList.shift()
}
// 发送给除了自己以外的其他所有用户
// socket.broadcast.emit("receive_message", messageList)
io.emit("receive_message", messageList)
})
})

http.listen(2000, _ => {
console.log('This server is running: http://localhost:2000');
})

静态的文件是使用 create-react-app 开发的页面,build之后放到了服务器上面dist目录下面。

要使用socket.io,首先需要创建socket服务

1
var io = require('socket.io')(http);

接下来就是连接服务端与客户端了。服务端如果想要连接到客户端的用户,那么就需要有方法一直监听到客户端用户访问网站的方法。socket.io中就为我们提供了一个 connection 方法。

1
2
3
io.on("connection", socket => {
// do something
})

connection 之后所有的操作都是写在这个 connection 的监听之中。

上面的 connection 中的代码需要注意的有几点,知道了这几点,那么socket.io对你就不是难事

  1. io.on('监听事件名字', () => {})方法是监听所有的用户。
  2. connection方法中的 socket 值得是当前用户,所以socket.on('监听事件名字', () => {})是监听当前用户的操作。
  3. io.emit('监听事件名字', 参数)是发送消息给客户端,此时客户端会有一个监听的事件,监听事件名字服务端需要与客户端相同。
  4. socket.broadcast.emit("监听事件名字", 参数) 这个方法可以发送消息给除了自已以外的其他的所有的用户。

客户端与服务端一样。

记住以上四点秘诀,玩转socket不是梦。

客户端实现

客户端使用create-react-app写的页面实现,下面贴出逻辑,就不放样式了

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
142
143
144
import React, { Component } from 'react';
import './App.css';
import io from 'socket.io-client'
import { Input, Button, Avatar, message } from 'antd'

const socket = io('http://118.24.6.33:2000');

class App extends Component {
constructor(props){
super(props)

this.state = {
showLogin: true,
users: {
userCount: 0,
userList: {}
},
messageList: []
}
}

login = () => {
const username = this.refs.input.input.value.trim()
const { userList } = this.state.users

if( username.length ){
for (const k in userList) {
if( userList[k] === username ){
message.info("聊天室已经有这个用户了,请重新起一个名字")
return
}
}

this.uid = this.get_uid()
socket.emit("login", {
username,
uid: this.uid
})

this.setState({
showLogin: false
})
} else {
message.info("请输入一个用户名!!")
}
}

get_uid = _ => {
return `${new Date().getTime()}${Math.floor(Math.random() * 89999)}`
}

send = _ => {
this.message = this.refs.message.input.value
if (this.message.trim().length === 0) {
message.info("你还啥子都还没有输入就行发送了嘛")
return
}
const id = `${new Date().getTime()}${Math.floor(Math.random() * 9999)}`
const data = {
message: this.message.trim(),
uid: this.uid,
id
}
socket.emit('message', data)

// ant design 中清空输入的内容
this.refs.message.state.value = ''
setTimeout(_ => this.refs.messages.scrollBy(0, 999999), 100)
}

componentDidMount(){
socket.on("users", data => {
this.setState({users: data})
})

socket.on("receive_message", data => {
this.setState({messageList: data})
})
}

render(){
const { showLogin, users, messageList } = this.state
const { userCount, userList } = users

if (showLogin) {
return (
<div className="App">
<Input placeholder="输入一个名字撒" allowClear ref='input' onPressEnter={this.login}/>
<Button onClick={this.login} className="login">登录</Button>
</div>
);
} else {
return (
<div className="room">
<div className='inner'>
<header>欢迎来到踏浪聊天室,当前聊天室共{userCount}人</header>
<div className="content">
<ul className="user-list">
{
Object.entries(userList).map(v => {
return <li
className="user-list-item"
key={v[0]}
>
<Avatar style={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>
{v[1].substring(0, 2)}
</Avatar>
{v[1]}
</li>
})
}
</ul>

<ul className="message-list" ref="messages">
{
messageList.map(v => <li
key={v.message.id}
className={v.message.uid === this.uid ? "message-list-item me" : "message-list-item"}
>
{v.message.uid === this.uid && <span className="message-content">{v.message.message}</span>}
<Avatar style={{ color: '#f56a00', backgroundColor: '#fde3cf' }}>
{v.username && v.username.substring(0, 2)}
</Avatar>
{v.message.uid !== this.uid && <span className="message-content">{v.message.message}</span>}
</li>)
}
</ul>
</div>
<footer>
<Input
placeholder="请输入消息"
ref='message'
onPressEnter={this.send}
/>
<Button className="send" onClick={this.send}>发送</Button>
</footer>
</div>
</div>
)
}
}
}

export default App

客户端可是使用的socket.io。不过使用的是专门为客户端提供的socket.io-client。客户端首先需要连接到服务器,通过 const socket = io('http://118.24.6.33:2000'); 就可以创建一个与服务端链接的 socket 请求。

接下来就是在 componentDidMount 中编写监听事件,同时 socket.on() 实现监听。

在事件中使用 socket.emit() 实现向后端发送消息。

整的逻辑的实现就是如此,摸清逻辑,后面的就不难了。

上面只是使用可socket.io的一些简单的API,关于更多的方法可以前往socket.io官网

最后,可以前往github查看源码

文章作者: 踏浪
文章链接: https://blog.lyt007.cn/技术/使用socket实现即时通讯聊天室.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 踏浪 - 前端技术分享
支付宝
微信打赏