zoukankan      html  css  js  c++  java
  • 习WebSocket一(WebSocket初识)[转]

    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技术。
  • 相关阅读:
    Oracle三大设计范式
    数据库查询4
    Oracle 常用内置函数
    数据库查询2
    数据库查询练习1
    Oracle 建表
    线程1—Runnable
    线程1—Thread
    输入输出2
    输入输出1
  • 原文地址:https://www.cnblogs.com/fx2008/p/4094538.html
Copyright © 2011-2022 走看看