【码歌】5分钟明白基于WebSocket的聊天室设计方案

WebSocket协议是H5的一个基于TCP的新协议。WebSocket协议被设计来取代用HTTP作为传输层的双向通讯技术,这些技术只能牺牲其中一方来提高另一方的效率和可依赖性。

效果展示

chat

chat

优点

在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:

​ 1. Header互相沟通的Header是很小的-大概只有 2 Bytes

​ 2. Server Push服务器的推送,服务器不再被动的接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。

应用

WebSocket可以解决很多需要实时通讯的问题,比如:聊天功能、社交订阅、股票基金变动、体育实况更新等等,都可以采用websocket进行解决。

协议内容

1544674093972

1544674093972

其中:

Upgrade: websocket
Connection: Upgrade

这等于告诉web服务器,发起的请求是Websocket协议的,要用Websocket协议方式来处理请求。在请求头里,还增加了:

Sec-WebSocket-Key: p+uL7rm/spX33U/iZ/yeZg== 
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Version: 13
  1. Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:验证是不是真的是Websocket助理;
  2. Sec-WebSocket-Extensions: 扩展参数;
  3. Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本)

聊天室编写

1、环境搭建

IDEA + MAVEN + JDK1.8 + Tomcat7,主要的jar包依赖如下:

<!-- Tomcat has it, so no need to package into the war file -->
<dependency>
  <groupId>javax.websocket</groupId>
  <artifactId>javax.websocket-api</artifactId>
  <version>${websocket.version}</version>
  <scope>provided</scope>
</dependency>

<!-- Sevlet jars for compilation, provided by Tomcat -->
<dependency>
  <groupId>org.apache.tomcat</groupId>
  <artifactId>tomcat-servlet-api</artifactId>
  <version>${tomcat.version}</version>
  <scope>provided</scope>
</dependency>

<!-- Used to serialize the message from the browser -->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.40</version>
</dependency>

2、前端js编写

思路:当用户点击登录时,使用websocket协议自动连接后台,大致流程如下:

官网地址:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

客户端的流程:

  1. 创建Websocket实例:

    var ws = new WebSocket("ws://127.0.0.1:8080/chat");

​ a. 服务器IP:ws://127.0.0.1

​ b. 服务器端口:8080

​ c. 服务程序:chat

  1. 建立连接后回调函数:ws.onopen = function(event){}
  2. 收到服务端消息后回调函数:ws.onmessage = function(event){}
  3. 关闭连接后回调函数:ws.onclose = function(event){}
  4. 连接错误后回调函数:ws.onerror = function(event)
  5. 发送数据:ws.send('服务器,您好');
  6. 查看当前连接状态:alert(ws.readyState)
var websocket, name;

// 建立连接
function connection() {
    name = $("#name").val();
    if (name == null || name.trim().length === 0) {
        return;
    }
    $("#username-page").addClass("hidden");
    $("#chat-page").removeClass("hidden");
    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://127.0.0.1:8080/chat");
    }else{
        alert('Not support websocket');
        return;
    }

    // 连接成功回调
    websocket.onopen = function (event) {
        play();
        $("#connecting").addClass("hidden");
        send('JOIN');
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        console.error("连接异常。。。")
    }

    // 接收到消息的回调方法
    websocket.onmessage = function(event){
        var text = event.data;
        play();
        onMessageReceived(text)
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        console.log("连接关闭。。。");
    }

}

// 发送消息
function send(type) {
    var text = $("#message").val();
    if (websocket == null) {
        return;
    }
    if ( type == 'CHAT' && (text ==null || text.trim().length == 0)) {
        return;
    }
    var content = {"sender":name, content: text, type: type};
    websocket.send(JSON.stringify(content));
    $("#message").val('');
    play();

}

// 声音
function play() {
    var audio = new Audio("/audio/beep.wav");
    audio.play();
}

// 处理接收的消息
function onMessageReceived(text) {
    var message = JSON.parse(text);
    var messageElement = $('<li></li>');

    if(message.type === 'JOIN' || message.type === 'LEAVE') {
        messageElement.addClass('event-message');
    } else {
        messageElement.addClass('chat-message');
        // 创建头像
        var avatarElement = $('<i></i>');
        avatarElement.html(message.sender[0]);
        var color = getAvatarColor(message.sender);
        avatarElement.css('background-color', color);
        messageElement.append(avatarElement);

        var usernameElement = $('<span></span>');
        usernameElement.html(message.sender);
        messageElement.append(usernameElement);
    }

    var textElement = $('<p></p>');
    textElement.html(message.content);

    messageElement.append(textElement);

    $('#messageArea').append(messageElement);
    $('#messageArea')[0].scrollTop = $('#messageArea')[0].scrollHeight;
}

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

// 随机获取颜色
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];
}

$(document).keydown(function(event){
    if (event.keyCode == 13) {
        send('CHAT');
    }
});

3、后台Java代码编写

Java关于WebSocket API官网地址:http://www.oracle.com/technetwork/cn/articles/java/jsr356-1937161-zhs.html

2011 年,IETF 将 WebSocket 协议标准化为 RFC 6455。从那时起,大多数 Web 浏览器都在实现支持 WebSocket 协议的客户端 API。而且,还开发了一些实现 WebSocket 协议的 Java 库。

@ServerEndpoint("/chat")
public class ChatEndPoint {

    private static Logger logger = Logger.getLogger(ChatEndPoint.class.getName());
    private static List<Session> sessions = new ArrayList<>();

    @OnOpen
    public void onOpen (Session session, EndpointConfig config) {
        logger.info("开启连接。。。");
        sessions.add(session);
    }

    @OnMessage
    public void onMessage(Session session, String text) throws IOException {
        logger.info("收到消息。。。");
        // 构建发送内容
        ChatMessage chatMessage = JSON.parseObject(text, ChatMessage.class);

        // 发送
        sendMessage(chatMessage.getSender(), chatMessage.getType(), chatMessage.getContent());

        // 把用户存入property
        Map<String, Object> properties = session.getUserProperties();
        properties.put("name", chatMessage.getSender());
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) throws IOException {
        logger.info("关闭连接,关闭原因:" + reason.getReasonPhrase());
        sessions.remove(session);
        Map<String, Object> properties = session.getUserProperties();
        String name = (String)properties.get("name");
        sendMessage(name, MessageType.LEAVE, null);
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        logger.info("异常处理:" + throwable.getMessage());
        throwable.printStackTrace();
    }

    /**
     * 消息推送
     */
    private void sendMessage(String name, MessageType type, String content) {
        if (type == MessageType.JOIN) {
            content = name + "-加入聊天室!";
        } else if (type == MessageType.LEAVE) {
            content = name + "-离开聊天室!";
        }
        ChatMessage chatMessage = new ChatMessage();
        chatMessage.setContent(content);
        chatMessage.setSender(name);
        chatMessage.setType(type);
        for (Session session: sessions) {
            if (!session.isOpen()) {
                continue;
            }
            try {
                session.getBasicRemote().sendText(JSON.toJSONString(chatMessage));
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }
}

4、执行tomcat7:run启动

chat

如果需要完整源码,请添加码歌悠悠q: 1811119218  直接获取,亲自体验。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值