WebSocket 聊天室示例

2019-03-22 23:43|来源: 网路

基本思路:
1、用户登录,把用户名存到session中,然后跳转到聊天页面chat.jsp
2、在chat.jsp加载完成后,向WebSocket服务端发起连接请求
3、WebSocket服务端接收到客户端请求后,把session存放在Set集合中,方便以后群聊的时候使用,同时向所有连接的客户端发起欢迎信息
4、实现群聊


用户登录页面,这里简单实现,只要输入一个用户名即可提交

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
<form action="loginServlet" method="post">
    用户名: <input name="username"/><br/>
    <input type="submit"/>
</form>
</body>
</html>


建立一个Servlet接收登录请求,并把该用户保存到Session中,然后跳转到添天页面
package com._656463.demo.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String username = request.getParameter("username");
        HttpSession session = request.getSession();
        session.setAttribute("user", username);
        response.sendRedirect("chat.jsp");
    }
}


web.xml 配置LoginServlet  
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>com._656463.demo.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/loginServlet</url-pattern>
</servlet-mapping>


聊天页面代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="keywords" content=""/>
    <meta name="description" content=""/>
    <script type="text/javascript">
        var user='${sessionScope.user}';
        var users=[];
        var ws;  // 管理登陆,退出,用户列表的 socket
        var ws2;  // 管理聊天 的 socket
        window.onload= ws_init;
        function  ws_init(){
            var target="ws://192.168.56.101:8080/chat?username="+user;
            if ('WebSocket' in window) {
                ws = new WebSocket(target);
            } else if ('MozWebSocket' in window) {
                ws = new MozWebSocket(target);
            } else {
                alert('WebSocket is not supported by this browser.');
                return;
            }
            ws.onopen=function(){
                showMsg("webSocket通道建立成功!!!");
            };
            ws.onmessage=function(event){
                ;
                console.info(msg);
                if(msg.welcome!=undefined)
                    showMsg(msg.welcome);
                if(msg.usernames!=undefined)
                    showUser(msg.usernames);
                if(msg.from!=undefined){
                    showContent(msg);
                }
            }
        }
        function showMsg(msg){
            var content= document.getElementById("content");
            content.innerHTML+=msg+"<br/>";
        }
        function showUser(users){
            if(users.length==0) return;
            var userList= document.getElementById("userList");
            userList.innerHTML="";
            for(var i=0;i<users.length;i++){
                userList.innerHTML+= users[i]+"<br/>";
            }
        }
        function showContent(msg){
            var content= document.getElementById("content");
            content.innerHTML+=msg.from+"--"+msg.date+"<br/>"+msg.content+"<br/>";
        }
        function ws_send(){
            var msg= document.getElementById("msg");
            var temp="{from:'"+user+"',content:'"+msg.value+"'}";
            ws.send(temp);
            msg.value="";
        }
    </script>
</head>
<body>
<h1>这里是聊天室 , 欢迎 ${sessionScope.user }进入聊天</h1>
<div id="content" style="background-color: aqua; width: 500px; height:400px;float:left;">
   <!-- 这个div显示所有的  聊天信息的内容 -->
</div>
<div id="userList" style="background-color: olive; width: 200px; height:400px;float: left;">
    <!--
        这个div 显示所有的在线用户
        这里要 显示两部分内容 是不是以为这要  两个 webSocket通道才能完成呢??
        一个通道能否完成!!
        回答:  两个webSocket通道 显然更加简单一些啊。。。
     -->
</div>
<div style="margin-top: 10px;  clear: both;">
    <input id="msg"/>
    <button onclick="ws_send();">send</button>
</div>
</body>
</html>


WebSocket实现聊天的服务端程序:
package com._656463.demo.websocket;
import com._656463.demo.vo.Msg;
import com.google.gson.Gson;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
/**
 * 聊天socket
 */
@ServerEndpoint("/chat")
public class ChatSocket {
    private static Set<ChatSocket> ss = new HashSet<ChatSocket>();
    private static List<String> usernames=new ArrayList<String>();
    private Session  session ;
    private String username;
    private Gson gson=new Gson();
    @OnOpen
    public void open(Session session) {
        System.out.println("建立了一个 webSocket通道!!!  sid:" + session.getId());
        String queryString = session.getQueryString();
        this.username = queryString.substring(queryString.indexOf("=") + 1);
        ss.add(this);
        usernames.add(username);
        this.session = session;
        Map<String,Object> welcomeMsg = new HashMap<String,Object>();
        welcomeMsg.put("welcome",username + "进入聊天室!!");
        welcomeMsg.put("usernames",usernames);
        //login对象转为  json格式
        String msg = gson.toJson(welcomeMsg);
        broadcast(ss, msg);
    }
    @OnClose
    public void close() {
        ss.remove(this);
        usernames.remove(username);
        Map<String,Object> logoutMsg = new HashMap<String,Object>();
        logoutMsg.put("welcome",username + "退出聊天室!!");
        logoutMsg.put("usernames",usernames);
        String msg = gson.toJson(logoutMsg);
        broadcast(ss, msg);
    }
    @OnMessage
    public void receiveMsg(Session session, String content) {
        System.out.println("收到信息啦 , 来自sid:" + session.getId());
        System.out.println("信息:" + content);
        Msg msg = gson.fromJson(content, Msg.class);
        msg.setDate(new Date().toLocaleString());
        broadcast(ss, gson.toJson(msg));
    }
    private void broadcast(Set<ChatSocket> ss, String msg) {
        for (Iterator iterator = ss.iterator(); iterator.hasNext(); ) {
            ChatSocket chat = (ChatSocket) iterator.next();
            try {
                chat.session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


相关问答

更多
  • 有一种方式是通过脚本语言在客户端通过判断消息的一些标识,选择性的显示消息,这种方式实现起来简单但是不够安全,看什么场合用了。
  • 在服务器的选择上很广,基本上,主流语言都有WebSocket的服务器端实现,而我们作为前端开发工程师,当然要选择现在比较火热的NodeJS作为我们的服务器端环境了。 NodeJS本身并没有原生的WebSocket支持,但是有第三方的实现(大家要是有兴趣的话,完全可以参考WebSocket协议来做自己的实现),我们选择了“ws”作为我们的服务器端实现。 由于本文的重点是讲解WebSocket,所以,对于NodeJS不做过多的介绍,不太熟悉的朋友可以去参考NodeJS入门指南(http://www.nodebe ...
  • 据推测, accept是一个阻塞函数调用,所以forever的使用不是一个分叉炸弹。 至于为什么你这样做,嗯,我认为这是一个相当标准的做法,一个程序监听套接字接受所需的连接数量。 我不是100%确定是否有“Haskell电池”用于代码中缺少的部分,但是在Hackage中肯定有几个包含其名称或描述中的websocket; 你应该检查一下然后发一个更具体的问题。 Presumably accept is a blocking function call, so the use of forever isn't ...
  • 我认为第二种方法更适合用于性能。 我正在使用相同的应用程序,但它仍处于测试阶段,因此无法对实时性能发表评论。 现在它运行10-15组并且工作正常。 在我的应用程序中,存在类似于您的用户可以基于组进行聊天的情况。 我正在使用node.js在服务器端处理组创建。 以下是创建组的代码,但它适用于我的应用程序特定条件。 只是粘贴在这里作为参考。 从前端获取homeState和userId 。 基于homeState创建组。 此代码仅作为示例,它不适合您。 要提高性能,您可以使用clustering 。 this.C ...
  • 简短的答案是“两者”。 XMPP是用于进行实时聊天(以及许多其他事情)的一组应用程序协议,然后必须通过网络传输,因此您需要传输绑定。 XMPP有三种主要的传输绑定 - TCP / IP,这是通常在Internet上使用的设备上的本机客户端 HTTP(称为BOSH),这是浏览器中使用XMPP时传统使用的(因为浏览器中的JavaScript应用程序不可用TCP-IP) Websockets是在现代浏览器中进行XMPP时使用的。 因此,如果您正在浏览器中开发聊天应用程序,那么您可以选择XMPP作为应用协议,您可以 ...
  • Websockets用于使用HTTP协议在服务器和客户端之间进行通信。 它们是任何跨语言/跨平台实时流/消息传递相关任务的特别合适的解决方案,因为客户端/消费者在任何新数据到达时立即得到通知,而无需轮询**。 浏览器实现规范的客户端部分。 大多数服务器端语言都有实现服务器部分的库。 如果您想要基本的即时消息,它就像常规聊天一样简单:您只需将聊天室中的每个人的收件人列表更改为该对话的特定客户端。 如果您想构建一个生产级即时消息应用程序,您应该知道您不必重新发明轮子:您可以在任何XMPP库之上构建您的Messe ...
  • 感谢nlucaroni,在进一步阅读后,我创建了一个具体的例子来回答我的问题。 #require "websocket";; #require "lwt";; #require "lwt.syntax";; (* Set up the uri address *) let ws_addr = Uri.of_string "ws://echo.websocket.org" (* Set up the websocket connection *) l ...
  • 我使它与--async=100和--ugreen选项一起工作。 请注意,您可以使用更新的API: 本机WebSocket支持和基于通用基于gevent的异步支持 。 I made it to work with --async=100 and --ugreen options. Note that there are newer API's that you can use: native WebSocket support and generic gevent-based async support.
  • 所以这并不像人们想象的那么直截了当。 基本上我使用的调度程序线程就像所有连接客户端的控制中心一样。 因此,每当客户端收到消息时,它就会发送给调度程序,然后将消息分发给每个连接的客户端。 我还必须在另一个线程中接收消息,因为没有非阻塞方式来接收rust-websocket中的消息。 然后,我可以使用永久循环来检查从websocket和调度程序接收的新消息。 以下是我的代码最终的样子: extern crate websocket; use std::str; use std::sync::{Arc, Mut ...
  • 如果你刷新页面它将需要重新连接,我不认为有办法解决它。 我建议不要使用页面加载在页面之间导航,而是建立单页面应用程序。 如果你不能以任何方式做到这一点,也许你可以将你的应用程序放在一个框架中,外部的html将有套接字。 这可能有用。 If you refresh the page it will need to reconnect, i dont think there is a way around it. I would recommend not using page-load to navigate ...