zoukankan      html  css  js  c++  java
  • 如何用WebSocket实现一个简单的聊天室以及单聊功能

    百度百科中这样定义WebSocket:WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。简单的说,WebSocket协议之前,双工通信是通过多个http链接来实现,这导致了效率低下,而WebSocket解决了这个问题。

    1.1 思考: 传统web的请求和响应模式中, 我们如何实现实时信息传输, 如何实现服务器反推数据?
        在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能.
        可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。在JavaEE7中也实现了WebSocket协议。

    1.2 WebSocket的目标:**打破传统的web请求响应模型, 实现管道式的实时通信。打开一个浏览器和服务器的通信通道,持续连接! 服务器给浏览器推送数据 非常方便!web的实时消息通信: 聊天,股票,游戏,监控等等。**
    1.3  软件需求
            (1). 安装jdk7 或更高版本
            (2). 下载tomcat7   两者保持一致(32、64位)
    
    2.1 实现一个webSocket应用程序,我们要学会几个基本操作。
            (1).  开启连接
            (2).  客户端给服务器端发送数据
            (3).  服务器端接收数据
            (4).  服务器端给客户端发送数据
            (5).  客户端接收数据
            (6).  监听三类基本事件: onopen,onmessage,onclose
            提示: onmessage 是发送数据时的响应事件。onopen是打开连接时的响应事件。onclose是关闭连接时的响应事件。

    3.1 接下来正式实现聊天室功能,首先写一个登录页面

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Insert title here</title>
    <script type="text/javascript"  src="jquery-1.4.4.min.js"></script>
    </head>
    <body>
        <form  name="ff"  action="LoginServlet" method="post" >
            用户名:<input name="username"  /><br/>
            <input type="submit"  value="登录"/>
        </form>
    </body>
    </html>

    3.2 在web.xml配置LoginServlet

    <servlet>
        <description></description>
        <display-name>LoginServlet</display-name>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.bjsxt.servlet.LoginServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/LoginServlet</url-pattern>
      </servlet-mapping>

    3.3 添加LoginServlet,用于处理登录请求以及跳转到聊天室界面

    package com.bjsxt.servlet;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class LoginServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request,
                  HttpServletResponse response)throws IOException,ServletException{
    
        }
    
    
        public void doPost(HttpServletRequest request,
                  HttpServletResponse response)throws IOException,ServletException{
    
            String username = request.getParameter("username");
            System.out.println("doPost当前登录用户为"+username);
            request.getSession().setAttribute("username",username);
            //这里只是简单地模拟登录,登陆之后直接跳转到聊天页面
            response.sendRedirect("chat.jsp");
        }
    }

    3.4 添加聊天界面

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Insert title here</title>
    <script type="text/javascript" src="jquery-1.4.4.min.js"></script>
    <script type="text/javascript">
        var ws;
        var userName = ${sessionScope.username};
        //通过URL请求服务端(chat为项目名称)
        var url = "ws://localhost:8080/chat/chatSocket?username=${sessionScope.username}";
    
        //进入聊天页面就是一个通信管道
        window.onload = function() {
            if ('WebSocket' in window) {
                ws = new WebSocket(url);
            } else if ('MozWebSocket' in window) {
                ws = new MozWebSocket(url);
            } else {
                alert('WebSocket is not supported by this browser.');
                return;
            }
    
            //监听服务器发送过来的所有信息
            ws.onmessage = function(event) {
                eval("var result=" + event.data);
    
                //如果后台发过来的alert不为空就显示出来
                if (result.alert != undefined) {
                    $("#content").append(result.alert + "<br/>");
                }
    
                //如果用户列表不为空就显示
                if (result.names != undefined) {
                    //刷新用户列表之前清空一下列表,免得会重复,因为后台只是单纯的添加
                    $("#userList").html("");
                    $(result.names).each(
                            function() {
                                $("#userList").append(
                                        "<input  type=checkbox  value='"+this+"'/>"
                                                + this + "<br/>");
                            });
                }
    
                //将用户名字和当前时间以及发送的信息显示在页面上
                if (result.from != undefined) {
                    $("#content").append(
                            result.from + " " + result.date + " 说:<br/>"
                                    + result.sendMsg + "<br/>");
                }
    
            };
        };
    
        //将消息发送给后台服务器
        function send() {
            //拿到需要单聊的用户名
            //alert("当前登录用户为"+userName);
            var ss = $("#userList :checked");
            //alert("群聊还是私聊"+ss.size());
            var to = $('#userList :checked').val();
            if (to == userName) {
                alert("你不能给自己发送消息啊");
                return;
            }
            //根据勾选的人数确定是群聊还是单聊
            var value = $("#msg").val();
            //alert("消息内容为"+value);
            var object = null;
            if (ss.size() == 0) {
                object = {
                    msg : value,
                    type : 1, //1 广播 2单聊    
                };
            } else {
                object = {
                    to : to,
                    msg : value,
                    type : 2, //1 广播 2单聊    
                };
            }
            //将object转成json字符串发送给服务端
            var json = JSON.stringify(object);
            //alert("str="+json);
            ws.send(json);
            //消息发送后将消息栏清空
            $("#msg").val("");
        }
    </script>
    </head>
    <body>
    
        <h3>欢迎 ${sessionScope.username }使用本聊天系统!!</h3>
    
        <div id="content"
            style="border: 1px solid black;  400px; height: 300px; float: left; color: #7f3f00;"></div>
        <div id="userList"
            style="border: 1px solid black;  120px; height: 300px; float: left; color: #00ff00;"></div>
    
        <div style="clear: both;" style="color:#00ff00">
            <input id="msg" />
            <button onclick="send();">发送消息</button>
        </div>
    </body>
    </html>

    3.5 添加启动类

    package com.bjsxt.init;
    
    import java.util.Set;
    
    import javax.websocket.Endpoint;
    import javax.websocket.server.ServerApplicationConfig;
    import javax.websocket.server.ServerEndpointConfig;
    /**
     * 项目启动时会自动启动,类似与ContextListener.
     * 是webSocket的核心配置类。
     * @author xiongzichao
     *
     */
    public class ServerConfig implements ServerApplicationConfig {
    
        //扫描src下所有类@ServerEndPoint注解的类。
        @Override
        public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scan) {
            System.out.println("扫描到"+scan.size()+"个服务端程序");
            return scan;
        }
    
        //获取所有以接口方式配置的webSocket类。
        @Override
        public Set<ServerEndpointConfig> getEndpointConfigs(
                Set<Class<? extends Endpoint>> point) {
            System.out.println("实现EndPoint接口的类数量:"+point.size());
            return null;
        }
    
    }

    3.6 添加客户端发送给服务端消息实体

    package com.bjsxt.vo;
    /**
     * 客户端发送给服务端消息实体
     * @author xiongzichao
     *
     */
    public class ContentVo {
    
        private String to;
        private String msg;
        private Integer type;
        public String getTo() {
            return to;
        }
        public void setTo(String to) {
            this.to = to;
        }
        public String getMsg() {
            return msg;
        }
        public void setMsg(String msg) {
            this.msg = msg;
        }
        public Integer getType() {
            return type;
        }
        public void setType(Integer type) {
            this.type = type;
        }
    
    }

    3.7 添加服务端发送给客户端消息实体

    package com.bjsxt.vo;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * 服务端发送给客户端消息实体
     * @author xiongzichao
     *
     */
    public class Message {
    
        private  String  alert;   //
    
        private  List<String>  names;
    
        private  String  sendMsg;
    
        private String  from;
    
        private String  date;
    
    
        public String getDate() {
            return date;
        }
    
        public void setDate(String date) {
            this.date = date;
        }
    
        public String getSendMsg() {
            return sendMsg;
        }
    
        public void setSendMsg(String sendMsg) {
            this.sendMsg = sendMsg;
        }
    
        public String getFrom() {
            return from;
        }
    
        public void setFrom(String from) {
            this.from = from;
        }
    
        public String getAlert() {
            return alert;
        }
    
        public void setAlert(String alert) {
            this.alert = alert;
        }
    
        public List<String> getNames() {
            return names;
        }
    
        public void setNames(List<String> names) {
            this.names = names;
        }
    
        public Message() {
            super();
        }
    }

    3.8 添加服务端程序

    package com.bjsxt.server;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    import com.bjsxt.vo.ContentVo;
    import com.bjsxt.vo.Message;
    import com.google.gson.Gson;
    
    /**
     * 总通信管道
     * @author xiongzichao
     *
     */
    @ServerEndpoint("/chatSocket")
    public class ChatSocket {
    
        //定义一个全局变量集合sockets,用户存放每个登录用户的通信管道
        private  static  Set<ChatSocket>  sockets=new HashSet<ChatSocket>();
        //定义一个全局变量Session,用于存放登录用户的用户名
        private  Session  session;
        //定义一个全局变量map,key为用户名,该用户对应的session为value
        private  static  Map<String, Session>  map=new HashMap<String, Session>();
        //定义一个数组,用于存放所有的登录用户,显示在聊天页面的用户列表栏中
        private  static  List<String>names=new ArrayList<String>();
        private String username;
        private Gson  gson=new Gson();
    
        /*
         * 监听用户登录
         */
        @OnOpen
        public void open(Session session){
            this.session = session;
            //将当前连接上的用户session信息全部存到scokets中
            sockets.add(this);
            //拿到URL路径后面所有的参数信息
            String queryString = session.getQueryString();
            System.out.println();
            //截取=后面的参数信息(用户名),将参数信息赋值给全局的用户名
            this.username = queryString.substring(queryString.indexOf("=")+1);
            //每登录一个用户,就将该用户名存入到names数组中,用于刷新好友列表
            names.add(this.username);
            //将当前登录用户以及对应的session存入到map中
            this.map.put(this.username, this.session);
            System.out.println("用户"+this.username+"进入聊天室");
            Message message = new Message();
            message.setAlert("用户"+this.username+"进入聊天室");
            //将当前所有登录用户存入到message中,用于广播发送到聊天页面
            message.setNames(names);
            //将聊天信息广播给所有通信管道(sockets)
            broadcast(sockets, gson.toJson(message) );
        }
    
        /*
         * 退出登录
         */
        @OnClose
        public void close(Session session){
            //移除退出登录用户的通信管道
            sockets.remove(this);
            //将用户名从names中剔除,用于刷新好友列表
            names.remove(this.username);
            Message message = new Message();
            System.out.println("用户"+this.username+"退出聊天室");
            message.setAlert(this.username+"退出当前聊天室!!!");
            //刷新好友列表
            message.setNames(names);
            broadcast(sockets, gson.toJson(message));
        }
    
        /*
         * 接收客户端发送过来的消息,然后判断是广播还是单聊
         */
        @OnMessage
        public void receive(Session  session,String msg) throws IOException{
            //将客户端消息转成json对象
            ContentVo vo = gson.fromJson(msg, ContentVo.class);
            //如果是群聊,就像消息广播给所有人
            if(vo.getType()==1){
                Message message = new Message();
                message.setDate(new Date().toLocaleString());
                message.setFrom(this.username);
                message.setSendMsg(vo.getMsg());
                broadcast(sockets, gson.toJson(message));
            }else{
                Message message = new Message();
                message.setDate(new Date().toLocaleString());
                message.setFrom(this.username);
                message.setAlert(vo.getMsg());
                message.setSendMsg("<font color=red>正在私聊你:</font>"+vo.getMsg());
                String to  = vo.getTo();
                //根据单聊对象的名称拿到要单聊对象的Session
                Session to_session = this.map.get(to);
                //如果是单聊,就将消息发送给对方
                to_session.getBasicRemote().sendText(gson.toJson(message));
            }
        }
    
        /*
         * 广播消息
         */
        public void broadcast(Set<ChatSocket>sockets ,String msg){
            //遍历当前所有的连接管道,将通知信息发送给每一个管道
            for(ChatSocket socket : sockets){
                try {
                    //通过session发送信息
                    socket.session.getBasicRemote().sendText(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    4.0 实现界面 
    具体实现界面如图

    注意

        这里一共需要三个jar包,分别是WebSocket-api.jar这个定义webSocket应用程序开发接口!tomcat7-webSocket.jar tomcat服务器对于webSocket接口的实现!!还有一个gson.jar用于序列化实体类信息。前面两个jar包在tomcat7的lib目录里面可以找到。
  • 相关阅读:
    线段树(区间合并) LA 3989 "Ray, Pass me the dishes!"
    BIT LA 4329 Ping pong
    并查集(路径更新) LA 3027 Corporative Network
    log4j.properties配置详解与实例-全部测试通过
    ActiveMQ入门实例
    activemq安全设置 设置admin的用户名和密码
    Freemarker入门小案例(生成静态网页的其中一种方式)
    maven项目在myeclipse中不出现Maven Dependencies 和maven标识的解决方法
    Mysql,SqlServer,Oracle主键自动增长的设置
    常用SQL语句
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/10242796.html
Copyright © 2011-2022 走看看