java websocket实现聊天室 附源码

目录

1.Socket基础知识

2.socket代码实现

2.1 引入依赖

2.2 配置websocket

2.3 websocket的使用

2.4 webSocket服务端模块

2.5 前端代码

3.测试发送消息

4.websocket源码地址


1.Socket基础知识

Socket(套接字)用于描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发出请求或者应答网络请求。

Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必需的5种信息:连接所使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址以及远地进程的协议端口。
 

2.socket代码实现

2.1 引入依赖

我这里使用了swagger,方便调试

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!--springboot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--swagger包-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

2.2 配置websocket

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author yt
 * @create 2023/4/20 13:45
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

2.3 websocket的使用

前端传一个用户id,将用户id和对应的session进行绑定,一对一就是客户端根据对应的用户id将消息发送给对应的session

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * https://blog.csdn.net/weixin_56079712/article/details/121602008
 *
 * @author yt
 * @create 2023/4/20 13:46
 */

@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);


    /**
     * 记录当前在线连接数
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);


    /**
     * 用户id  对应的session
     */
    private static Map<String, Session> userIdToSession = new ConcurrentHashMap<>();
    /**
     * session  对应的用户id
     */
    private static Map<Session, String> SessionTouserId = new ConcurrentHashMap<>();


    /**
     * 创建一个数组用来存放所有需要向客户端发送消息的窗口号
     */
    private static List<String> list = new ArrayList();

    public static List<String> getList() {
        return list;
    }


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        // 在线数加1
        onlineCount.incrementAndGet();
        userIdToSession.put(userId, session);
        SessionTouserId.put(session, userId);
        list.add(userId);
        String msg = userId + "连接成功";
        sendMessage(msg, session);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        //在线连接-1
        onlineCount.decrementAndGet();
        String userId = SessionTouserId.get(session);
        list.remove(userId);
        logger.info(userId + "断开连接,当前连接数为 " + onlineCount);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("客户端发送消息为" + message);
        sendMessage(message, session);
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println(" 发生错误 ");
        error.printStackTrace();
    }

    /**
     * 服务端发送消息给客户端
     */
    public void sendMessage(String message, Session session) {
        try {
            logger.info("服务端给客户端[{}]发送消息[{}]", session.getId(), message);
            session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            logger.error("服务端发送消息给客户端失败:{}", e);
        }
    }

    public void sendOneMessage(String message, String userId) {
        try {
            Session session = userIdToSession.get(userId);
            if (session != null && session.isOpen()) {
                logger.info("服务端给客户端[{}]发送消息[{}]", session.getId(), message);
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            logger.error("服务端发送消息给客户端失败:{}", e);
        }
    }

    public void sendAllMessage(String message) {
        try {
            if (list.size() < 1) {
                logger.info("当前在线人数为0,发送消息为" + message);
                return;
            }
            for (String userId : list) {
                Session session = userIdToSession.get(userId);
                if (session != null && session.isOpen()) {
                    logger.info("服务端给客户端[{}]发送消息[{}]", session.getId(), message);
                    session.getBasicRemote().sendText(message);
                }
            }

        } catch (Exception e) {
            logger.error("服务端发送消息给客户端失败:{}", e);
        }
    }

}

2.4 webSocket服务端模块

import com.yt.websocket.websocket.WebSocketServer;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Api(tags = "webSocket服务端模块")
@RestController
@RequestMapping("/test")
public class SendMessageController {

    @Autowired
    private WebSocketServer webSocketServer;

    @ApiOperation(value = "群发消息")
    @GetMapping("/sendMsg/{msg}")
    public String sendAllMsg(@PathVariable("msg") String msg) {
        webSocketServer.sendAllMessage(msg);
        return "群发消息【" + msg + "】发送成功";
    }

    @ApiOperation(value = "给单个用户发送消息")
    @GetMapping("/sendOneMsg/{msg}/{userId}")
    public String sendOneMsg(@PathVariable("msg") String msg, @PathVariable("userId") String userId) {
        webSocketServer.sendOneMessage(msg, userId);
        return "消息【" + msg + "】给 " + userId + "发送成功";
    }

    @ApiOperation(value = "获取在线用户")
    @GetMapping("/get/userId")
    public List getUserId() {
        return WebSocketServer.getList();
    }
}

2.5 前端代码

<!DOCTYPE html>
<meta charset="utf-8"/>
<title>WebSocket 测试 乔丹</title>
<body>
<h2>WebSocket 测试 乔丹</h2>
<HEADER class="header">
    <a class="back" ></a>
    <h3 class="tit">服务端:</h3>
</HEADER>
<div id="message">

</div>

<HEADER class="header1">
    <a class="back" ></a>
    <h3 class="tit">客户端:</h3>
</HEADER>

<div id="footer">
    <input id="text" class="my-input" type="text" />
    <button onclick="send()" >发&nbsp;送</button>
</div>

<div id="footer1">
    <br/>
    <button onclick="closeWebSocket()" >关闭websocket连接</button>
    <button onclick="openWebSocket()" >建立websocket连接</button>
</div>


<script language="javascript" type="text/javascript">
    var websocket = null;
    //判断当前浏览器是否支持WebSocket,是则创建WebSocket
    if ('WebSocket' in window) {
        console.log("浏览器支持Websocket");
        websocket = new WebSocket("ws://localhost:9091/websocket/乔丹");
    } else {
        alert('当前浏览器 Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
        console.log("WebSocket连接发生错误");
        setMessageInnerHTML("WebSocket连接发生错误");
    };
    //连接成功建立的回调方法
    websocket.onopen = function () {
//	setMessageInnerHTML("WebSocket连接成功");
        console.log("WebSocket连接成功");
    }
    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        if (event.data) {
            setMessageInnerHTML(event.data);
        }
        console.log(event.data);
    }
    //连接关闭的回调方法
    websocket.onclose = function () {
        console.log("WebSocket连接关闭");
    }

    //关闭WebSocket连接
    function closeWebSocket() {
        websocket.close();
    }

    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }


    // 建立连接的方法
    function openWebSocket() {
        websocket = new WebSocket("ws://localhost:8888/websocket/乔丹");
        websocket.onopen = function () {
//	setMessageInnerHTML("WebSocket连接成功");
            console.log("WebSocket连接成功");
        }
    }


    //将消息显示在网页上

    function setMessageInnerHTML(innerHTML) {

        document.getElementById('message').innerHTML += innerHTML + '<br/>';

    }


    //如果websocket连接还没断开就关闭了窗口,后台server端会抛异常。
    //所以增加监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接
    window.onbeforeunload = function () {
        closeWebSocket();
    }

</script>
</body>
<div id="output"></div>
</html>

3.测试发送消息

用户端:

服务端:

用户端成功接收到消息

 

4.websocket源码地址

https://gitee.com/yutao0730/web-socket-chat-room.git

猜你喜欢

转载自blog.csdn.net/m0_58709145/article/details/130387488
今日推荐