学习WebSocket一(WebSocket初识)
            时间:2014-07-09 23:58:13  
            收藏:0  
            阅读:825
        
        
        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初识),布布扣,bubuko.com
原文:http://www.cnblogs.com/wgp13x/p/3812579.html
            评论(0)
        
        
        
        



