http://www.cnblogs.com/wgp13x/p/3812579.html

Java EE 7 去年刚刚发布了JSR356规范,使得WebSocket的Java API得到了统一,Tomcat从7.0.47开始支持JSR356,这样一来写WebSocket的时候,所用的代码都是可以一样的。今天终于体验了一把Tomcat发布的WebSocket,用着很爽,下面把这一历程分享给大家。

关键词:WebSocket, Tomcat

前提:使用Tomcat7.0.47,Firefox25.0.0.5046

首先Tomcat7.0.47自带WebSocket的示例程序,有两种版本,一种是使用注解(annotation API)的方式,另一种是继承javax.websocket.Endpoint类(programmatic API)。
先启动一下,看看效果。有四个example:Echo(回音)、Chat(聊天)、Multiplayer snake(多人蛇游戏)、Multiplayer drawboard(多人画板游戏)。
1、Echo
回音很简单,就是你输入什么,服务器给你返回什么。
界面效果​ 服务端代码
打开一个页面,首先点击Connect,保证连接到Websocket,
再在输入框里输入"I am angel1!",
点击Echo message,可以看到下面框里输入Sent和Received信息。
 

我们看一下它的代码是怎么实现的。
可以看出,在OnMessage处就是简单的通过传来的Session得到某一客户端,
再向其发出同样的消息。

@ServerEndpoint("/websocket/echoAnnotation")
public class EchoAnnotation {
 
    @OnMessage
    public void echoTextMessage(Session session, String msg, boolean last) {
        try {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(msg, last);
            }
        } catch (IOException e) {
            try {
                session.close();
            } catch (IOException e1) {
                // Ignore
            }
        }
    }
 
    @OnMessage
    public void echoBinaryMessage(Session session, ByteBuffer bb,
            boolean last) {
        try {
            if (session.isOpen()) {
                session.getBasicRemote().sendBinary(bb, last);
            }
        } catch (IOException e) {
            try {
                session.close();
            } catch (IOException e1) {
                // Ignore
            }
        }

}
}

2、Chat
这里的聊天室跟群聊是同一个效果,不能一对一单聊。
界面效果​ 服务端代码
打开第一个页面,它会告诉你,你已经加入聊天了。
分析代码,就是一个新连接,会自动实例化一个ChatAnnotation,
这些ChatAnnotation对象共用同一些属性,
最重要的就是Set<ChatAnnotation> conncetions,
在OnOpen处把自身实例加入到conncetions中,并广播消息。
广播消息,是轮循conncetions并发送消息。

在界面输入对话框处输入文字,回车,消息就会发送到服务端。
就会传入到服务端某ChatAnnotation的OnMessage处,
然后把收到的消息与自身名称拼接后,再广播出去。
这下在线的客户端就都能够收到消息了。

第一个页面:

第二个页面:

@ServerEndpoint(value = "/websocket/chat")
public class ChatAnnotation {
 
    private static final String GUEST_PREFIX = "Guest";
    private static final AtomicInteger connectionIds = new AtomicInteger(0);
    private static final Set<ChatAnnotation> connections =
            new CopyOnWriteArraySet<ChatAnnotation>();
 
    private final String nickname;
    private Session session;
 
    public ChatAnnotation() {
        nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
    }
 
 
    @OnOpen
    public void start(Session session) {
        this.session = session;
        connections.add(this);
        String message = String.format("* %s %s", nickname, "has joined.");
        broadcast(message);
    }
 
 
    @OnClose
    public void end() {
        connections.remove(this);
        String message = String.format("* %s %s",
                nickname, "has disconnected.");
        broadcast(message);
    }
 
 
    @OnMessage
    public void incoming(String message) {
        // Never trust the client
        String filteredMessage = String.format("%s: %s",
                nickname, HTMLFilter.filter(message.toString()));
        broadcast(filteredMessage);
    }
 
 
    private static void broadcast(String msg) {
        for (ChatAnnotation client : connections) {
            try {
                client.session.getBasicRemote().sendText(msg);
            } catch (IOException e) {
                connections.remove(client);
                try {
                    client.session.close();
                } catch (IOException e1) {
                    // Ignore
                }
                String message = String.format("* %s %s",
                        client.nickname, "has been disconnected.");
                broadcast(message);
            }
        }
    }

}

3、Multiplayer snake
这是一个多人在线小游戏,客户端通过操作上下左右键指挥自己的蛇,如果碰到别的蛇就死掉。还是一样,在服务端,对每个连接都维护一条蛇,有一个总的逻辑代码处理这些蛇,每条蛇再有各自的状态,向每个连接的客户发送消息。
@ServerEndpoint(value = "/websocket/snake")
public class SnakeAnnotation {
 
    public static final int PLAYFIELD_WIDTH = 640;
    public static final int PLAYFIELD_HEIGHT = 480;
    public static final int GRID_SIZE = 10;
 
    private static final AtomicInteger snakeIds = new AtomicInteger(0);
    private static final Random random = new Random();
 
 
    private final int id;
    private Snake snake;
public static String getRandomHexColor() {
        float hue = random.nextFloat();
        // sat between 0.1 and 0.3
        float saturation = (random.nextInt(2000) + 1000) / 10000f;
        float luminance = 0.9f;
        Color color = Color.getHSBColor(hue, saturation, luminance);
        return '#' + Integer.toHexString(
                (color.getRGB() & 0xffffff) | 0x1000000).substring(1);
    }
 
 
    public static Location getRandomLocation() {
        int x = roundByGridSize(random.nextInt(PLAYFIELD_WIDTH));
        int y = roundByGridSize(random.nextInt(PLAYFIELD_HEIGHT));
        return new Location(x, y);
    }
 
 
    private static int roundByGridSize(int value) {
        value = value + (GRID_SIZE / 2);
        value = value / GRID_SIZE;
        value = value * GRID_SIZE;
        return value;
    }
 
    public SnakeAnnotation() {
        this.id = snakeIds.getAndIncrement();
    }
@OnOpen
    public void onOpen(Session session) {
        this.snake = new Snake(id, session);
        SnakeTimer.addSnake(snake);
        StringBuilder sb = new StringBuilder();
        for (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator();
                iterator.hasNext();) {
            Snake snake = iterator.next();
            sb.append(String.format("{id: %d, color: '%s'}",
                    Integer.valueOf(snake.getId()), snake.getHexColor()));
            if (iterator.hasNext()) {
                sb.append(',');
            }
        }
        SnakeTimer.broadcast(String.format("{'type': 'join','data':[%s]}",
                sb.toString()));
    }
 
 
    @OnMessage
    public void onTextMessage(String message) {
        if ("west".equals(message)) {
            snake.setDirection(Direction.WEST);
        } else if ("north".equals(message)) {
            snake.setDirection(Direction.NORTH);
        } else if ("east".equals(message)) {
            snake.setDirection(Direction.EAST);
        } else if ("south".equals(message)) {
            snake.setDirection(Direction.SOUTH);
        }

}

 
 
    
 
 
    
 
 
    @OnClose
    public void onClose() {
        SnakeTimer.removeSnake(snake);
        SnakeTimer.broadcast(String.format("{'type': 'leave', 'id': %d}",
                Integer.valueOf(id)));
    }
 
 
    @OnError
    public void onError(Throwable t) throws Throwable {
        // Most likely cause is a user closing their browser. Check to see if
        // the root cause is EOF and if it is ignore it.
        // Protect against infinite loops.
        int count = 0;
        Throwable root = t;
        while (root.getCause() != null && count < 20) {
            root = root.getCause();
            count ++;
        }
        if (root instanceof EOFException) {
            // Assume this is triggered by the user closing their browser and
            // ignore it.
        } else {
            throw t;
        }

}
}

4、自写客户端
Multiplayer drawboard就不分析了,在Firefox25.0.0.5046上一直loading。下面探讨一下,Java客户端的编写。
界面和ClientEndpoit​
入口代码
下面是调用了echoAnnotation的websocket的客户端与服务端交互过程。
同样是客户端发给服务端一个消息,服务端收到后发给客户端,
客户端收到后显示出来。
 
 
客户端代码也很简单,没有什么逻辑,只管把接收的打印出来就行了。
需要注意的是,需要引用的jar包只在Java EE 7中包含。
包括javax.websocket-api.jar、tyrus-client.jar、
tyrus-container-grizzly.jar、tyrus-core.jar、
tyrus-websocket-core.jar、tyrus-spi.jar、tyrus-server.jar、
nucleus-grizzly-all.jar
 
同样的也可以调用其它的websocket,比如chat...使用起来非常方便。
 
 
@ClientEndpoint
public class MyClient {
 
    @OnOpen
    public void onOpen(Session session) {
 
    }
 
    @OnMessage
    public void onMessage(String message) {
        System.out.println("Client onMessage: " + message);
    }
 
    @OnClose
    public void onClose() {
 
    }
 

}

public class Main {
 
    private static String uri = "ws://localhost/examples/websocket/echoAnnotation";
    private static Session session;
 
    private void start() {
        WebSocketContainer container = null;
        try {
            container = ContainerProvider.getWebSocketContainer();
        } catch (Exception ex) {
            System.out.println("error" + ex);
        }
 
        try {
            URI r = URI.create(uri);
            session = container.connectToServer(MyClient.class, r);
        } catch (DeploymentException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Main client = new Main();
        client.start();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input = "";
        try {
            do {
                input = br.readLine();
                if (!input.equals("exit"))
                    client.session.getBasicRemote().sendText(input);
            } while (!input.equals("exit"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

这样以来,从功能少的说,搭建向手机、客户端推送消息的服务器是非常简单的;从功能多的说,以后搭建安全的、扩展性好的、性能超群的游戏服务器,比如QQ斗地主游戏、泡泡卡丁车游戏,在线聊天服务器,也是简单的。http://www.cnblogs.com/wgp13x/p/3800030.html  这是我之前的局域网多人对战飞行棋的实现,因为它是C#局域网版本的,故没有用到WebSocket技术。

习WebSocket一(WebSocket初识)[转]的更多相关文章

  1. 【WebSocket】WebSocket介绍

    1.背景 在WebSocket出现之前客户端向服务器发出请求是通过http协议实现的,而http协议有个特点是通行请求只能由客户端发起,然后服务端响应查询结果,HTTP 协议没法让服务器主动向客户端推 ...

  2. nginx支持websocket及websocket部分原理介绍

    nginx支持websocket及websocket部分原理介绍最近ipc通过websocket与server进行通行,经过无法通过nginx进行反向代理,只有直连nodejs端口.而且部署到阿里云用 ...

  3. 【WebSocket】WebSocket快速入门

    WebSocket介绍 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动 ...

  4. 【WebSocket】WebSocket消息推送

    准备使用WebSocket实现Java与Vue或者安卓间的实时通信,实现私密聊天.群聊.查询下资料备用. WebSocket客户端 websocket允许通过JavaScript建立与远程服务器的连接 ...

  5. WebSocket(1)---WebSocket介绍

    WebSocket介绍   一.为什么需要 WebSocket? 初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处? 答案很 ...

  6. 【HTML5 WebSocket】WebSocket对象特性和方法

    <HTML5 WebSocket权威指南>学习笔记&3 WebSocket方法的对象特性 1. WebSocket方法 a. send方法 send方法用于在WebSocket连接 ...

  7. 借助FreeHttp任意篡改Websocket报文(Websocket改包)

    前言 作为Web应用中最常见的数据传输协议之一的Websocket,在我们日常工作中也势必会经常使用到,而在调试或测试中我们常常也有直接改变Websocket数据报文以确认其对应用的影响的需求,本文将 ...

  8. 初识WebSocket协议

    1.什么是WebSocket协议 RFC6455文档的表述如下: The WebSocket Protocol enables two-way communication between a clie ...

  9. 【HTML5】websocket 初识

    什么是WebSocket API? WebSocket API是下一代客户端-服务器的异步通信方法.该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序.WebSock ...

  10. WebSocket协议详解及应用

    WebSocket协议详解及应用(七)-WebSocket协议关闭帧 本篇介绍WebSocket协议的关闭帧,包括客户端及服务器如何发送并处理关闭帧.关闭帧错误码及错误处理方法.本篇内容主要翻译自RF ...

随机推荐

  1. Django 基础

    Django 的路由系统 在 django 的 URLconf 配置文件 urls.py 中根据一个 URL 对应 views 的一个函数来处理用户的请求. 1.基本的 urls 对应 urlpatt ...

  2. php mysql PDO使用

    <?php $dbh = new PDO('mysql:host=localhost;dbname=access_control', 'root', ''); $dbh->setAttri ...

  3. WINDOWS下简单操作SQLITE3

    有测试操作的时候,还是很好的说~~~ 找个sqlite3.txt下载 sqlite3.exe db.sqlite3 SQLite version 3.7.13 2012-06-11 02:05:22 ...

  4. MYSQLl防注入

    1.简单sql防注入 简述: 所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令. 在某些表单中,用户输入的内容直接用来构造(或 ...

  5. Android MAVEN项目标准目录结构

    1.标准目录结构: src -main      –bin 脚本库      –java java源代码文件      –resources 资源库,会自动复制到classes目录里      –fi ...

  6. 内存管理(memory allocation内存分配)

    Memory management is the act of managing computer memory. The essential requirement of memory manage ...

  7. mysql 累加排序求名次

    自己做的一个小项目里需要用mysql计算一些信息. mysql中的 表如下(注:表中数据都是测试数据,随机生成的) mysql> select * from shake_log; +-----+ ...

  8. 一起啃PRML - Preface 前言

    一起啃PRML - 前言 Preface @copyright 转载请注明出处 http://www.cnblogs.com/chxer/ PRML,Pattern Recognition and M ...

  9. Linux下高效编写Shell——shell特殊字符汇总

    Linux下无论如何都是要用到shell命令的,在Shell的实际使用中,有编程经验的很容易上手,但稍微有难度的是shell里面的那些个符号,各种特殊的符号在我们编写Shell脚本的时候如果能够用的好 ...

  10. [转载] cookie、JS记录及跳转到页面原来的位置

    额....如下 <!-- 定位页面的 Cookie function SetCookie(sName, sValue) { date = new Date(); s = date.getDate ...