SpringBoot + websocket + React(群聊系统)

展示

SpringBoot + websocket + React(群聊系统)_第1张图片
SpringBoot + websocket + React(群聊系统)_第2张图片
SpringBoot + websocket + React(群聊系统)_第3张图片
页面不是很优美,但是功能全了,拿到源码的可以自己去美化一下就可以

1 - SpringBoot环境搭建
1 - maven 依赖

    <parent>
        <artifactId>spring-boot-starter-parentartifactId>
        <groupId>org.springframework.bootgroupId>
        <version>2.3.5.RELEASEversion>
    parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>
        <dependency>
            <groupId>com.github.ulisesbocchiogroupId>
            <artifactId>jasypt-spring-boot-starterartifactId>
            <version>3.0.3version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
        dependency>
    dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>

        <resources>
            
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
            resource>

            
            <resource>
                <directory>src/main/resourcesdirectory>
                <includes>
                    <include>**/*.*include>
                includes>
            resource>

            
            <resource>
                <directory>src/main/webappdirectory>
                <targetPath>META-INF/resourcestargetPath>
                <includes>
                    <include>**/*.*include>
                includes>
            resource>
        resources>
    build>

2 - socket

@Slf4j //lombok jar包,帮我们自动生成一些代码:@Data
@Component
@ServerEndpoint("/websocket/{username}")
public class ChatServerEndpoint {
    public static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();
    /**
     * 连接建立时触发
     */
    @OnOpen
    public synchronized void openSession(@PathParam("username") String username, Session session) {
        String message = "[" + username + "]登录";
        //存放到map集合中
        CLIENTS.put(username,session);
        //告诉自己当前在线的人数
        Set<String> userNames = CLIENTS.keySet();
        userNames.forEach(u -> {
            try {
                session.getBasicRemote().sendText("[" + u + "]登录");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        //告诉所有人
        CLIENTS.forEach((u,s) -> {
            try {
                if (s != session)  //上面我已经告诉自己了  所以不能再告诉自己了
                    s.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 客户端接收服务端数据时触发
     */
    @OnMessage
    public synchronized void onMessage(@PathParam("username") String username, String message) {
        log.info("发送消息:{}, {}", username, message);
        //告诉所有人发消息
        String value = "["+username+"]:"+ message;
        CLIENTS.forEach((u,s) -> {
            try {
                s.getBasicRemote().sendText(value);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 连接关闭时触发
     */
    @OnClose
    public synchronized void onClose(@PathParam("username") String username, Session session) {
        // 当前的Session移除某个用户
        CLIENTS.remove(username);
        //离开消息通知所有人有人离开了
        CLIENTS.forEach((u,s) -> {
            try {
                s.getBasicRemote().sendText("[" + username + "]离开");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 通信发生错误时触发
     */
    @OnError
    public synchronized void onError(Session session, Throwable throwable) {
        try {
            //关闭WebSocket Session会话
            System.out.println("被摧毁");
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("onError Exception", e);
        }
        log.info("Throwable msg " + throwable.getMessage());
    }
}

3 - 配置类

@EnableWebSocket //启用WebSocket支持
@Configuration 
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

4 - controller 获取随机用户名

@RestController
@CrossOrigin
public class ChatController {

    private AtomicInteger idProducer = new AtomicInteger();

    @RequestMapping("/getName")
    public String index(Model model) {
        return "user" +idProducer.getAndIncrement();
    }
}

2 - React搭建

App.jsx

import { useEffect, useRef, useState } from "react";
import './App.css';
import axios from 'axios';
import Right from "./components/Right";
import Left from "./components/Left";
import { notification} from 'antd';
import { Avatar,Comment, TextArea, Form, Button, List, Input } from 'antd';


let websocket;
let username

function App() {
    const messageRef = useRef(null)
    const [messageList,setMessageList] = useState([])
    const [message,setMessage] = useState("")
    const [userList,setUserList] = useState([])
    const { TextArea } = Input;
    const data = [
        {
            render:(item) => {
                return <>item</>
            }
        }
    ]
    useEffect(() => {
        async function start() {
            if (!localStorage.getItem('username')) {
                await axios.get("http://localhost:8080/getName").then(response => {
                    localStorage.setItem('username', response.data)
                })
            }
            username = localStorage.getItem('username')
            let baseUrl = "ws://localhost:8080/websocket/"
            websocket = new WebSocket(baseUrl + localStorage.getItem('username'));
    
            websocket.onopen =  ()=> {
                console.log("建立 websocket 连接...");
            };
            websocket.onmessage = (event) => {
                const data = event.data
                setMessage(data)
            };
            websocket.onerror =  (event) => {
                console.log("websocket发生错误..." + event + '\n');
            }
    
            websocket.onclose =  ()=> {
                console.log("关闭 websocket 连接...");
            };
        }
        start()
    },[])
    useEffect(() => {
        console.log(message)
        if (message.indexOf(":") > 0) {
            setMessageList([...messageList,message])
            console.log(messageList)
            setMessage("")
            return 
        }
        if (message.indexOf("登录") > 0) {
            setUserList([...userList.filter(item => {
                return item !== message
            }),message])
            notification.info({
                message: `${message}`,
                description: ``,
                placement:'topLeft'
            });
            setMessage("")
            return
        }
        if (message.indexOf('离开') > 0) {
            let messageUsername = message.substr(0,message.indexOf("]") + 1)
            setUserList([...userList.filter(item => item.indexOf(messageUsername) < 0 )])
            notification.info({
                message: `${message}`,
                description: ``,
                placement:'topLeft'
            });
            setMessage("")
            return
        }
    },[message])


    const sendMessage = () => {
        const message = messageRef.current.value
        if (message.trim() === "") {
            alert("请重新输入")
            return
        }
        messageRef.current.value = ""
        websocket.send(message)
    }
    return (
        <>
            <div className="header">
                <h2>聊天室</h2>
            </div>
            <div className="container">
                <div className="chart">
                    {
                        messageList.map(item => {
                            if (item.indexOf(username) > 0) {
                                console.log(username)
                                return <Right value={item} key={item + Math.random()} />
                            } else {
                                return <Left value={item} key={item + Math.random()}/>

                            }
                        })
                    }
                </div>
                <div className="input-value">
                    <textarea ref={messageRef} type="text" placeholder="发送消息" />
                    <button onClick={sendMessage}>发送</button>
                </div>
            </div>
            <div className="online">

                <span>当前在线人数 {userList.length}</span>
                {
                    userList.map(item => {
                        return <h2 key={Math.random()}>{item}</h2>
                    })
                }
            </div>
        </>
    )

}


export default App;

App.css

@import '~antd/dist/antd.css';
.header {
    width: 100%;
    height: 50px;
    background-color: #008c8c;
    text-align: center;
    line-height: 50px;
}

.container {
    position: absolute;
    margin-top: 50px;
    background-color: aqua;
    width: 80%;
    top: 0;
    bottom: 0;
}
.chart {
    position: absolute;
    top: 0;
    width: 100%;
    height: 70%;
    background-color: #fff;
    padding: 20px;
    overflow: scroll;

}

.input-value{
    width: 100%;
    height: 30%;
    bottom: 0;
    position: absolute;
    background-color: #008c8c;
}

.input-value textarea {
    width: 100%;
    height: 80%;
    border: 0;
    padding: 20px;
}
.input-value button {
    width: 100%;
    border: 0;
    height: 15%;
}

.online{
    position: absolute;
    margin-top: 50px;
    background-color: aqua;
    width: 20%;
    top: 0;
    bottom: 0;
    right: 0;
}

左边组件

import React from "react";
function Left(props) {
    let value = props.value
    const username = value.substr(0,value.lastIndexOf("]") + 1)
    const message = value.substr(value.lastIndexOf(":") + 1);
    return (
        <>
            <div className="item">
                <div>
                    <div style={{fontSize:'15px',color:'#ccc',textAlign:'left'}}>{username}</div>
                    <div style={{padding:'10px',textAlign:'left'}}>{message}</div>
                </div>
            </div>
        </>
    ) 
}

export default Left;

右边组件

import React from "react";
function Right(props) {
    let value = props.value
    const username = value.substr(0,value.lastIndexOf("]") + 1)
    const message = value.substr(value.lastIndexOf(":") + 1);
    return (
        <>
            <div className="item">
                <div>
                    <div style={{fontSize:'15px',color:'#ccc',textAlign:'right'}}>{username}</div>
                    <div style={{padding:'10px',textAlign:'right'}}>{message}</div>
                </div>
            </div>
        </>
    ) 
}

export default Right;

你可能感兴趣的:(笔记,spring,boot,websocket,react.js)