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

  • 2019 年 11 月 28 日
  • 笔记

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

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

Demo地址

准备工作

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

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

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

服务端实现

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

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服务

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

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

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写的页面实现,下面贴出逻辑,就不放样式了

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查看源码