【Websocket】JAVA群聊、一对一聊天室

STOMP协议:STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。

STOMP发送帧如下形式:

>>> SEND
transaction:tx-0
destination:/app/marco
content-length:20

{"message":"Marco!"}

群聊

0、ChatMessage实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage implements Serializable {
    private MessageType type;
    private String content;
    private String sender;
    private String receiver;
}

MessageType枚举

public enum MessageType {
    CHAT,
    JOIN,
    LEAVE
}

1、webSocketCofig

@Configuration
//开启websocket服务器
@EnableWebSocketMessageBroker
@Slf4j
public class WebSocketConfig extends ServerEndpointConfig.Configurator implements WebSocketMessageBrokerConfigurer {

    /**
     * STOMP 简单文本消息传递协议
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册websocket端点,连接websocket服务器
        //withSockJS() 为了启用不支持websocket的浏览器
        registry.addEndpoint("/ws").withSockJS();
    }

    /**
     * 消息代理:将消息从一个客户端路由到另一个客户端
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //消息到路由时,处理的方法
//        registry.setApplicationDestinationPrefixes("/app");
        //方法1:
        //路由到消息代理时,向订阅特定主题的客户端广播消息
//        registry.enableSimpleBroker("/topic");
        //方法2:use rabbitmq
        registry.enableStompBrokerRelay("/topic")
                .setRelayHost("localhost")
                //rabbitmq要先开启stomp插件,该插件默认端口为61313
                .setRelayPort(61613)
                .setClientLogin("guest")
                .setClientPasscode("guest");
    }
}

注:如何开启rabbitmq stomp

1.进入到RabbitMQ安装目录下的sbin文件夹内

2.执行命令

rabbitmq-plugins enable rabbitmq_web_stomp
rabbitmq-plugins enable rabbitmq_web_stomp_examples

2、 WebSocketEventListener

@Component
@Slf4j
public class WebSocketEvenListener {
    //实现自由向任意目的地发送消息,并且订阅此目的地的所有用户都能收到消息
    @Autowired
    private SimpMessageSendingOperations messageSendingOperations;
    //建立websocket连接
    @EventListener
    public void handleSocketConnectListener(SessionConnectedEvent event){
        log.info("received a socket connect");
    }
    //断开连接
    public void handleSocketDisconnectListener(SessionConnectedEvent event){
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        String username=(String)headerAccessor.getSessionAttributes().get("username");
        if (StringUtils.isEmpty(username)){
            log.info("user disconnect:{}",username);

            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setType(MessageType.LEAVE);
            chatMessage.setSender(username);
            //广播发送订阅了/topic/public通道的用户
            messageSendingOperations.convertAndSend("/topic/public",chatMessage);
        }
    }
}

3、ChatController

@Controller
@Slf4j
public class ChatController {

    @Autowired
    private SimpMessagingTemplate template;

    /**
     * 广播消息
     * @SendTo相当于
     * this.template.convertAndSend("/topic/public",chatMessage);
     * 发送消息之该通道
     * @param chatMessage
     * @return
     */
    @MessageMapping("/chat.sendMessage")
    @SendTo("/topic/public")
    public ChatMessage sendMessage(@Payload ChatMessage chatMessage){
        log.info("{}用户对你说{}",chatMessage.getSender(),chatMessage.getContent());
        return chatMessage;
    }

    /**
     * 广播了用户加入事件
     * @param chatMessage
     * @param headerAccessor
     * @return
     */
    @MessageMapping("/chat.addUser")
    @SendTo("/topic/public")
    public ChatMessage addUser(@Payload ChatMessage chatMessage,
                               SimpMessageHeaderAccessor headerAccessor){
        //保存websocket session
        headerAccessor.getSessionAttributes().put("username",chatMessage.getSender());
        return chatMessage;
    }

}

前端页面

1、index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <title>Spring Boot WebSocket Chat Application</title>
    <link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<noscript>
    <h2>Sorry! Your browser doesn't support Javascript</h2>
</noscript>

<div id="username-page">
    <div class="username-page-container">
        <h1 class="title">Type your username</h1>
        <form id="usernameForm" name="usernameForm">
            <div class="form-group">
                <input type="text" id="name" placeholder="Username" autocomplete="off" class="form-control" />
            </div>
            <div class="form-group">
                <button type="submit" class="accent username-submit">Start Chatting</button>
            </div>
        </form>
    </div>
</div>


<div id="chat-page" class="hidden">
    <div class="chat-container">
        <div class="chat-header">
            <h2>Spring WebSocket Chat Demo</h2>
        </div>
        <div class="connecting">
            Connecting...
        </div>
        <ul id="messageArea">

        </ul>
        <form id="messageForm" name="messageForm">
            <div class="form-group">
                <div class="input-group clearfix">
                    <input type="text" id="message" placeholder="Type a message..." autocomplete="off" class="form-control"/>
                    <button type="submit" class="primary">Send</button>
                </div>
            </div>
        </form>
        <br>
    </div>
</div>

<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js" charset="utf-8"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="/js/main.js"></script>
</body>
</html>

2、main.css

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

html,body {
    height: 100%;
    overflow: hidden;
}

body {
    margin: 0;
    padding: 0;
    font-weight: 400;
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 1rem;
    line-height: 1.58;
    color: #333;
    background-color: #f4f4f4;
    height: 100%;
}

body:before {
    height: 50%;
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    background: #128ff2;
    content: "";
    z-index: 0;
}

.clearfix:after {
    display: block;
    content: "";
    clear: both;
}

.hidden {
    display: none;
}

.form-control {
    width: 100%;
    min-height: 38px;
    font-size: 15px;
    border: 1px solid #c8c8c8;
}

.form-group {
    margin-bottom: 15px;
}

input {
    padding-left: 10px;
    outline: none;
}

h1, h2, h3, h4, h5, h6 {
    margin-top: 20px;
    margin-bottom: 20px;
}

h1 {
    font-size: 1.7em;
}

a {
    color: #128ff2;
}

button {
    box-shadow: none;
    border: 1px solid transparent;
    font-size: 14px;
    outline: none;
    line-height: 100%;
    white-space: nowrap;
    vertical-align: middle;
    padding: 0.6rem 1rem;
    border-radius: 2px;
    transition: all 0.2s ease-in-out;
    cursor: pointer;
    min-height: 38px;
}

button.default {
    background-color: #e8e8e8;
    color: #333;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
}

button.primary {
    background-color: #128ff2;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
    color: #fff;
}

button.accent {
    background-color: #ff4743;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
    color: #fff;
}

#username-page {
    text-align: center;
}


.username-page-container {
    background: #fff;
    box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
    border-radius: 2px;
    width: 100%;
    max-width: 500px;
    display: inline-block;
    margin-top: 42px;
    vertical-align: middle;
    position: relative;
    padding: 35px 55px 35px;
    min-height: 250px;
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    margin: 0 auto;
    margin-top: -160px;
}



.usernameusername-page-container .username-submit {
    margin-top: 10px;
}




#chat-page {
    position: relative;
    height: 100%;
}

.chat-container {
    max-width: 700px;
    margin-left: auto;
    margin-right: auto;
    background-color: #fff;
    box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
    margin-top: 30px;
    height: calc(100% - 60px);
    max-height: 600px;
    position: relative;
}

#chat-page ul {
    list-style-type: none;
    background-color: #FFF;
    margin: 0;
    overflow: auto;
    overflow-y: scroll;
    padding: 0 20px 0px 20px;
    height: calc(100% - 150px);
}

#chat-page #messageForm {
    padding: 20px;
}

#chat-page ul li {
    line-height: 1.5rem;
    padding: 10px 20px;
    margin: 0;
    border-bottom: 1px solid #f4f4f4;
}

#chat-page ul li p {
    margin: 0;
}

#chat-page .event-message {
    width: 100%;
    text-align: center;
    clear: both;
}

#chat-page .event-message p {
    color: #777;
    font-size: 14px;
    word-wrap: break-word;
}

#chat-page .chat-message {
    padding-left: 68px;
    position: relative;
}

#chat-page .chat-message i {
    position: absolute;
    width: 42px;
    height: 42px;
    overflow: hidden;
    left: 10px;
    display: inline-block;
    vertical-align: middle;
    font-size: 18px;
    line-height: 42px;
    color: #fff;
    text-align: center;
    border-radius: 50%;
    font-style: normal;
    text-transform: uppercase;
}

#chat-page .chat-message span {
    color: #333;
    font-weight: 600;
}

#chat-page .chat-message p {
    color: #43464b;
}

#messageForm .input-group input {
    float: left;
    width: calc(100% - 85px);
}

#messageForm .input-group button {
    float: left;
    width: 80px;
    height: 38px;
    margin-left: 5px;
}

.chat-header {
    text-align: center;
    padding: 15px;
    border-bottom: 1px solid #ececec;
}

.chat-header h2 {
    margin: 0;
    font-weight: 500;
}

.connecting {
    padding-top: 5px;
    text-align: center;
    color: #777;
    position: absolute;
    top: 65px;
    width: 100%;
}


@media screen and (max-width: 730px) {

    .chat-container {
        margin-left: 10px;
        margin-right: 10px;
        margin-top: 10px;
    }
}

@media screen and (max-width: 480px) {
    .chat-container {
        height: calc(100% - 30px);
    }

    .username-page-container {
        width: auto;
        margin-left: 15px;
        margin-right: 15px;
        padding: 25px;
    }


    #chat-page ul {
        height: calc(100% - 120px);
    }

    #messageForm .input-group button {
        width: 65px;
    }

    #messageForm .input-group input {
        width: calc(100% - 70px);
    }

    .chat-header {
        padding: 10px;
    }

    .connecting {
        top: 60px;
    }

    .chat-header h2 {
        font-size: 1.1em;
    }
}

3、main.js

'use strict';

var usernamePage = document.querySelector('#username-page');
var chatPage = document.querySelector('#chat-page');
var usernameForm = document.querySelector('#usernameForm');
var messageForm = document.querySelector('#messageForm');
var messageInput = document.querySelector('#message');
var messageArea = document.querySelector('#messageArea');
var connectingElement = document.querySelector('.connecting');


var stompClient = null;
var username = null;
var receiverName = null;


var colors = [
    '#2196F3', '#32c787', '#00BCD4', '#ff5652',
    '#ffc107', '#ff85af', '#FF9800', '#39bbb0'
];


function connect(event) {
    username = document.querySelector('#name').value.trim();

    if(username) {
        usernamePage.classList.add('hidden');
        chatPage.classList.remove('hidden');
        //建立websocket连接
        var socket = new SockJS('/ws');
        stompClient = Stomp.over(socket);

        stompClient.connect({}, onConnected, onError);
    }
    event.preventDefault();
}


function onConnected() {
    //订阅监听/topic/public通道
    stompClient.subscribe('/topic/public', onMessageReceived);

    //添加用户
    stompClient.send("/chat.addUser",
        {},
        JSON.stringify({sender: username, type: 'JOIN'})
    )

    connectingElement.classList.add('hidden');
}



function onError(error) {
    connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!';
    connectingElement.style.color = 'red';
}


function sendMessage(event) {
    var messageContent = messageInput.value.trim();
    console.log("messagecontent:"+messageContent);
    if(messageContent && stompClient) {
        var chatMessage = {
            sender: username,
            receiver: receiverName,
            content: messageInput.value,
            type: 'CHAT'
        };
        //发送消息至/app/chat.sendMessage接口
        stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(chatMessage));
        console.log("have send"+ JSON.stringify(chatMessage))
        messageInput.value = '';
    }
    event.preventDefault();
}


//监听
function onMessageReceived(payload){
    console.log("receive success");
    console.log("payload:"+payload);
    var message = JSON.parse(payload.body);
    console.log("message:"+message);
    var messageElement = document.createElement('li');

    if(message.type === 'JOIN') {
        messageElement.classList.add('event-message');
        message.content = message.sender + ' joined!';
    } else if (message.type === 'LEAVE') {
        messageElement.classList.add('event-message');
        message.content = message.sender + ' left!';
    } else {
        messageElement.classList.add('chat-message');

        var avatarElement = document.createElement('i');
        var avatarText = document.createTextNode(message.sender[0]);
        avatarElement.appendChild(avatarText);
        avatarElement.style['background-color'] = getAvatarColor(message.sender);

        messageElement.appendChild(avatarElement);

        var usernameElement = document.createElement('span');
        var usernameText = document.createTextNode(message.sender);
        usernameElement.appendChild(usernameText);
        messageElement.appendChild(usernameElement);
    }
    var textElement = document.createElement('p');
    var messageText = document.createTextNode(message.content);
    textElement.appendChild(messageText);

    messageElement.appendChild(textElement);

    messageArea.appendChild(messageElement);
    messageArea.scrollTop = messageArea.scrollHeight;
}


function getAvatarColor(messageSender) {
    var hash = 0;
    for (var i = 0; i < messageSender.length; i++) {
        hash = 31 * hash + messageSender.charCodeAt(i);
    }
    var index = Math.abs(hash % colors.length);
    return colors[index];
}



usernameForm.addEventListener('submit', connect, true)
messageForm.addEventListener('submit', sendMessage, true)


一对一聊天

前端与群聊相似,下面只介绍JAVA的改变

1、ChatServer 

2、WebSocketEventListener都不用改

3、ChatController

@Controller
@Slf4j
public class ChatController {

    @Autowired
    private SimpMessagingTemplate template;

    //发送消息到通道,同时订阅对方和自己的通道
    @MessageMapping("/one.sendMessage")
    public ChatMessage sendMessage2(@Payload ChatMessage chatMessage){
        //对对方
        this.template.convertAndSend("/topic/"+chatMessage.getReceiver(),chatMessage);
        //对自己
        this.template.convertAndSend("/topic/"+chatMessage.getSender(),chatMessage);
        return chatMessage;
    }

    //添加用户
    @MessageMapping("/one.addUser")
    @SendTo("/user/queue/getResponse")
    public ChatMessage addUser2(@Payload ChatMessage chatMessage,
                               SimpMessageHeaderAccessor headerAccessor){
        //session
        headerAccessor.getSessionAttributes().put("username",chatMessage.getSender());
        return chatMessage;
    }

 }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中实现一对一聊天的 WebSocket 可以使用 Java API for WebSocket (JSR 356) 实现。以下是简单的实现步骤: 1. 创建一个 WebSocketServerEndpoint 类,继承 javax.websocket.Endpoint 类。 2. 在类中实现有 @OnOpen, @OnMessage, @OnClose 三个注解的方法,分别用于处理 WebSocket 的打开、消息接收和关闭事件。 3. 在 @OnOpen 中,使用 session.getBasicRemote().sendText() 方法向客户端发送欢迎消息。 4. 在 @OnMessage 中,使用 session.getId() 获取当前 session 的 id,然后遍历 sessionList,找到对应的接收端 session,使用接收端 session.getBasicRemote().sendText() 方法向接收端发送消息。 5. 在 @OnClose 中,从 sessionList 中删除当前关闭的 session。 以下是一个简单的示例: ```java import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/chat") public class WebSocketServerEndpoint { private static List<Session> sessionList = new ArrayList<>(); @OnOpen public void onOpen(Session session) throws IOException { sessionList.add(session); session.getBasicRemote().sendText("Welcome to chat!"); } @OnMessage public void onMessage(String message, Session session) throws IOException { String sessionId = session.getId(); for (Session s : sessionList) { if (s.getId().equals(sessionId)) { continue; } s.getBasicRemote().sendText(message); } } @OnClose public void onClose(Session session) { sessionList.remove(session); } } ``` 这是一个简单的实现,你可以根据需要进行改进。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值