继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

《java websocket》之 实现

手插口袋_
关注TA
已关注
手记 25
粉丝 455
获赞 3117

作者: 手插口袋_ 笔名:晓枫 [请尊重原创,盗版必究,转载请指明出处]

上一篇《java websocket》之 描述篇介绍了一下websocket,那么这一次就自己动手搭建一个属于自己的websocket。

真的挺简单的

环境描述:java jdk1.7+tomcat7.0x

第一步。创建一个干净的web project工程:
web工程
第二步。然后导入我们所用到的包:
jar
第三步。创建一个websocket类用来实现接口,在创建一个POOL类用来扩展业务
class
废话不说直接上代码,相信我,我提供的代码直接复制进去就可以用!

WebSocket 类:

package websocket;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

public class WebSocket extends WebSocketServer {

    public WebSocket(InetSocketAddress address) {
        super(address);
        System.out.println("地址"+address);
    }

    public WebSocket(int port) throws UnknownHostException {
        super(new InetSocketAddress(port));
        System.out.println("端口"+port);
    }
    /**
     * 触发关闭事件
     */
    @Override
    public void onClose(org.java_websocket.WebSocket conn, int message,
            String reason, boolean remote) {
        userLeave(conn);
    }
    /**
     * 触发异常事件
     */
    @Override
    public void onError(org.java_websocket.WebSocket conn, Exception message) {
        System.out.println("Socket异常:"+message.toString());
        e++;
    }
    /**
     * 客户端发送消息到服务器时触发事件
     */
    int j=0;
    int h=0;
    int e=0;
    int l=0;
    @Override
    public void onMessage(org.java_websocket.WebSocket conn, String message) {
        message = message.toString();
        if(message!=null){
            //将用户加入
            this.userjoin(message.toString(), conn);
        }
    }
    /**
     * 触发连接事件
     */
    @Override
    public void onOpen(org.java_websocket.WebSocket conn, ClientHandshake handshake) {
        System.out.println("有人连接Socket conn:"+conn);
        l++;
    }
    /**
     * 用户加入处理
     * @param user
     */
    public void userjoin(String user, org.java_websocket.WebSocket conn) {
        WebSocketPool.sendMessage(user); // 把当前用户加入到所有在线用户列表中
        String joinMsg = "[系统]"+user+"上线了!";
        WebSocketPool.sendMessage(joinMsg); // 向所有在线用户推送当前用户上线的消息
        WebSocketPool.addUser(user, conn); // 向连接池添加当前的连接对象
        WebSocketPool.sendMessageToUser(conn, WebSocketPool.getOnlineUser().toString()); // 向当前连接发送当前在线用户的列表
    }
    /**
     * 用户下线处理
     * @param user
     */
    public void userLeave(org.java_websocket.WebSocket conn) {
        String user = WebSocketPool.getUserByKey(conn);
        boolean b = WebSocketPool.removeUser(conn); // 在连接池中移除连接
        if (b) {
            WebSocketPool.sendMessage(user.toString()); // 把当前用户从所有在线用户列表中删除
            String joinMsg = "[系统]"+user+"下线了";
            WebSocketPool.sendMessage(joinMsg); // 向在线用户发送当前用户退出的消息
        }
    }
}

WebSocketPool 类:

package websocket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.java_websocket.WebSocket;

public class WebSocketPool {
    private static final Map<WebSocket, String> userconnections = new HashMap<WebSocket, String>();

    /**
     * 获取用户名
     * @param session
     */
    public static String getUserByKey(WebSocket conn) {
        return userconnections.get(conn);
    }

    /**
     * 获取在线总数
     * @param
     */
    public static int getUserCount() {
        return userconnections.size();
    }

    /**
     * 获取WebSocket
     * @param user
     */
    public static WebSocket getWebSocketByUser(String user) {
        Set<WebSocket> keySet = userconnections.keySet();
        synchronized (keySet) {
            for (WebSocket conn : keySet) {
                String cuser = userconnections.get(conn);
                if (cuser.equals(user)) {
                    return conn;
                }
            }
        }
        return null;
    }

    /**
     * 向连接池中添加连接
     * @param inbound
     */
    public static void addUser(String user, WebSocket conn) {
        userconnections.put(conn, user); // 添加连接
    }

    /**
     * 获取所有的在线用户
     * @return
     */
    public static Collection<String> getOnlineUser() {
        List<String> setUsers = new ArrayList<String>();
        Collection<String> setUser = userconnections.values();
        for (String u : setUser) {
            setUsers.add(u);
        }
        return setUsers;
    }

    /**
     * 移除连接池中的连接
     * @param inbound
     */
    public static boolean removeUser(WebSocket conn) {
        if (userconnections.containsKey(conn)) {
            userconnections.remove(conn); // 移除连接
            return true;
        } else {
            return false;
        }
    }

    /**
     * 向特定的用户发送数据
     * @param user
     * @param message
     */
    public static void sendMessageToUser(WebSocket conn, String message) {
        if (null != conn) {
            conn.send(message);
        }
    }

    /**
     * 向所有的用户发送消息
     * @param message
     */
    public static void sendMessage(String message) {
        Set<WebSocket> keySet = userconnections.keySet();
        synchronized (keySet) {
            for (WebSocket conn : keySet) {
                String user = userconnections.get(conn);
                if (user != null) {
                    conn.send(message);
                }
            }
        }
    }
}

然后让我们先测试一下是否行得通:

所以我在WebSocket 类里面写一个main方法来启动websocket服务:

public static void main(String[] args) throws InterruptedException {
        System.out.println("开始启动websocket");
        WebSocketImpl.DEBUG = false;
        int port = 8888;  // 端口随便设置,只要不跟现有端口重复就可以
        WebSocket s = null;
        try {
            s = new WebSocket(port);
        } catch (UnknownHostException e) {
            System.out.println("启动websocket失败!");
            e.printStackTrace();
        }
        s.start();
        System.out.println("启动websocket成功!");
    }

启动成功后的样子

图片描述
然后去http://www.blue-zero.com/websocket/ WebSocket在线测试
输入地址跟端口后,点击链接
在线测试
有人连接
那么到了这一步就说明你的websocket 已经搭建好了哦!恭喜你。

下面跟着作者把配置好的websocket实际应用到项目中。

第一步:技术点为:项目启动的时候启动配置好的服务。

因为启动项目就要开启websocket服务保持长连接状态。所以我们找到了web.xml,注意必须是3.0以上的

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name> 
  <filter>
    <filter-name>startFilter</filter-name>
    <filter-class>filter.startFilter</filter-class>
  </filter>
    <servlet> 
        <servlet-name>TestServlet</servlet-name> 
        <servlet-class>servlet.TestServlet</servlet-class> 
    </servlet>
    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/TestServlet</url-pattern>
    </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

创建startFilter (原谅我忘记大写开头,自己以打自己脸3次),以及TestServlet(不懂Servlet出门百度)
filter

package filter;

import java.io.IOException;
import java.net.UnknownHostException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.java_websocket.WebSocketImpl;

import websocket.WebSocket;

/**
 * 创建人:晓枫 创建时间:2014年8月18日
 * @version
 */
public class startFilter implements Filter {

    /**
     * 初始化
     */
    public void init(FilterConfig fc) throws ServletException {
        this.startWebsocketOnline();
    }

    /**
     * 启动socket服务
     */
    public void startWebsocketOnline() {
        System.out.println("开始启动websocket");
        WebSocketImpl.DEBUG = false;
        int port = 8888;  // 端口随便设置,只要不跟现有端口重复就可以
        WebSocket s = null;
        try {
            s = new WebSocket(port);
                        s.start();
        } catch (UnknownHostException e) {
            System.out.println("启动websocket失败!");
            e.printStackTrace();
        }
        System.out.println("启动websocket成功!");
    }

    public void destroy() {
        // TODO Auto-generated method stub

    }

    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
        // TODO Auto-generated method stub

    }

}

细心地学者已经发现,实际上就是把main方法里面的代码拿过来了,霍哇啊哈哈哈哈哈哈!

TestServlet 类

package 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 TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("content"+req.getParameter("content"));
        req.getSession().setAttribute("content", req.getParameter("content"));
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }
}

到了这一步就基本服务端已经基本搭建好了。那么开始前台。技术为jsp页面+js

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">

    <title>晓枫的WebSocket</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">

    <script type="text/javascript" src="js/websocket.js"></script>
  </head>

  <body>
  <input type="hidden" value="${content }" id="content">
     <form action="TestServlet" method="post">
        content : <input type="text" name="content">
        <button type="submit">登陆</button>
     </form>
     <span id="span">

     </span>
  </body>
</html>

就简单的画了几个框框

废话: 大部分人都有个毛病,就是功能做一半就会被别的事情所打扰,导致事情拖延,然后加班。就比如一个简单的页面就可以测试,非要写的那么华丽!作者我不管在哪个公司,很少加班,因为是后台,界面的好看与否,本身不算到工作量里面。达到目的就可以。如果嫌弃丑会再给你时间改的,不会加班)

websocket.js

/**
 * 申请一个WebSocket对象,参数是需要连接的服务器端的地址,
 * 同http协议使用http://开头一样,
 * WebSocket协议的URL使用ws://开头,
 * 另外安全的WebSocket协议使用wss://开头
 * 创建人:晓枫 创建时间 2016-8-18
 */
var ws = new WebSocket("ws://127.0.0.1:8888");
ws.onopen = function()//当websocket创建成功时,即会触发onopen事件
{
var content=document.getElementById("content").value;
    if(content==nullcontent==""){
        ws.send("XiaoFeng");//用于叫消息发送到服务端  注:此处为用户名
    }else{
        ws.send(content);
    }
};
ws.onmessage = function(evt)//当客户端收到服务端发来的消息时,会触发onmessage事件,参数evt.data中包含server传输过来的数据
{
  document.getElementById("span").innerHTML="当前在线人数为:"+evt.data;
};
ws.onclose = function(evt)//当客户端收到服务端发送的关闭连接的请求时,触发onclose事件
{
  alert("WebSocketClosed!");
};
ws.onerror = function(evt)//如果出现连接,处理,接收,发送数据失败的时候就会触发onerror事件
{
  alert("WebSocketError!");
};

到这里就可以结束了。
下面正式展示一下

tomcat
1
11

我们打开多个页面来模仿多人连接.

2
22

我们输入一个内容
imooc
点击登录
imoocs
已经可以看到这个用户信息添加到Map了
同时,别的页面也收到了提示消息!
imooct

那么到这里就基本结束了。
强制下线就是把连接移除掉,并且通知你的logout方法就可以了。
只能一个人登陆的话,就判断一下Map里面是否有用户就OK了!
实例就在这里,注释也都有,自己开脑洞吧。

支持单聊哦!方法已经写好了在Pool.自己发现吧

到这里开始要开始研究高并发了,可能会穿插一些spring实现之类的,看情况吧。

(下面看不看吧,压力测试一下)

在线测试的网站可以下载压力测试工具
CS
然后稍微的修改一下代码。
XG
然后启动服务器,清空控制台,设置好5000个客户端同时连接,连接成功后发送“1”,项目接收到后回复“2”,也就是说发送次数到为 连接数X2 的时候为一个不丢失, 越接近越好。
500
开始战斗吧!
1400

额。。。。。。。。。。。。。。。。。。。。

呵呵。

zdn
zwl
学习是一种信仰,分享是一种态度!

打开App,阅读手记
85人推荐
发表评论
随时随地看视频慕课网APP

热门评论

哈哈,最后笑死我了T^T 多谢分享!

启动后没反应呢,代码不全呀

使用jar包:

    Java-WebSocket-1.4.0.jar

查看全部评论