`
rensanning
  • 浏览: 3515247 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:37505
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:604415
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:678191
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:87346
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:399876
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69090
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:90521
社区版块
存档分类
最新评论

基础知识 - WebSocket

 
阅读更多
WebSocket是一种新的协议,本质上和HTTP一样(握手连接等)。但它并不是在HTTP之上模拟推送,而是直接在TCP之上定义了帧Frame,实现客户端与服务器间的全双工通讯。

https://en.wikipedia.org/wiki/WebSocket

关于浏览器与服务器间的实时通讯,比较常见的方案是Polling轮询(Ajax)、Long Polling轮询(Comet)。也可以自己写Socket长连接,自定义协议,自己实现封包,拆包以及解决tcp粘包等问题。这些方案都比较麻烦。

    Polling
      |
     Comet
    /    \
   /      \
SSE    WebSocket



Polling、SSE、WebSocket的通讯流程:


WebSocket当初是作为HTML5的一部分,后来经过多次修订后独立出来 成为IETF的RFC 6455。目前大部分浏览器都支持WebSocket。 http://caniuse.com/#feat=websockets


(1)WebSocket组成:
WebSocket Protocol: IETF https://tools.ietf.org/html/rfc6455
WebSocket API: W3C https://www.w3.org/TR/2011/WD-websockets-20110929/

(2)WebSocket的subprotocol:
STOMP (Simple/Streaming Text Oriented Messaging Protocol) http://stomp.github.io/
WAMP (Web Application Messaging Protocol) http://wamp-proto.org/
XMPP (Extensible Messaging and Presence Protocol) https://xmpp.org/
AMQP (Advanced Message Queuing Protocol) https://www.amqp.org/
MQTT (Message Queue Telemetry Transport) http://mqtt.org/

(3)WebSocket特点:
从HTTP到WebScoket协议通过“Upgrade”建立连接
运行端口80/443,所以Proxy和Firewall是友好的 ws:// wss://
HTTP兼容的握手,基于Cookie的认证
并非纯正的TCP Socket,可以传输:UTF-8字符、二进制Frame
大大减少网络流量

(4)服务器端实现:
Java: JavaEE7实现了WebSocket协议(JSR 356)、Jetty
node.js: https://github.com/websockets/ws
         https://github.com/socketio/socket.io
PHP: https://github.com/ratchetphp/Ratchet/
Ruby: https://github.com/igrigorik/em-websocket

uWebSockets https://github.com/uWebSockets/uWebSockets
Undertow  http://undertow.io/index.html

(5)WebSocket API:
①JavaScript API
// Create a socket instance
var socket = new WebSocket('ws://localhost:8080');
// Open the socket
socket.onopen = function(event) {
        // Send an initial message
        socket.send('I am the client and I\'m listening!');
        // Listen for messages
        socket.onmessage = function(event) {
                console.log('Client received a message',event);
        };
        // Listen for socket closes
        socket.onclose = function(event) {
                console.log('Client notified socket has closed',event);
        };
        // To close the socket....
        //socket.close()
};


客户端兼容性测试:
if( window.WebSocket ){
  // supported
}else{
  // not supported
}


②Events:
ws.onopen = function(e) { };
ws.onmessage = function(e) { };
ws.onerror = function(e) { };
ws.onclose = function(e) { };

ws.addEventListener('message', onMessageHandler);
ws.addEventListener('open', onOpenHandler);
ws.addEventListener('close', onCloseHandler);

***on<eventname> 或 addEventListener() 都可以。

③Methods:
ws.send(data);
    // Sending String
    ws.send('your message');

    // Sending canvas ImageData as ArrayBuffer
    var img = canvas_context.getImageData(0, 0, 400, 320);
    var binary = new Uint8Array(img.data.length);
    for (var i = 0; i < img.data.length; i++) {
      binary[i] = img.data[i];
    }
    ws.send(binary.buffer);

    // Sending file as Blob (ws.binaryType = 'arraybuffer';)
    var file = document.querySelector('input[type="file"]').files[0];
    ws.send(file);

ws.close();
ws.close(1000, "Goodbye, World!"); // pass a code and a reason

④Attributes:
ws.readyState
ws.bufferedAmount
ws.protocol

(6)node.js的简单测试
server.js
var WebSocketServer = require('ws').Server,
    wss = new WebSocketServer({port: 8181});
wss.on('connection', function(ws) {
    console.log('client connected');
    ws.on('message', function(message) {
        console.log(message);
    });
});


client.html
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Echo Demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
    var ws = new WebSocket("ws://localhost:8181"); // wss:// (if using TLS)
    ws.onopen = function(e) {
      console.log('Connection to server opened');
    };
    ws.onerror = function (error) {
      console.log('WebSocket Error ' + error);
    };
    ws.onmessage = function (e) {
      console.log('Server: ' + e.data);
    };
    function sendMessage() {
      ws.send($('#message').val());
    }
</script>
</head>
<body>
    <div class="vertical-center">
        <div class="container">
          <p>&nbsp;</p>
          <form role="form" id="chat_form" onsubmit="sendMessage(); return false;">
            <div class="form-group">
              <input class="form-control" type="text" name="message" id="message" placeholder="Type text to echo in here" value="" autofocus/>
            </div>
            <button type="button" id="send" class="btn btn-primary" onclick="sendMessage();">Send!</button>
          </form>
        </div>
    </div>
    <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>


HTTP Headers
Handshake Request
Handshake Response
101 Switching Protocols
upgraded HTTP request




(7)Java测试 - Jetty WebSocket Example

@WebServlet(urlPatterns="/test")
public class WebSocketServletImpl extends WebSocketServlet {
    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.register(WebSocketSample.class);
    }
}

@WebSocket
public class WebSocketSample {
    @OnWebSocketConnect
    public void onConnect(Session session) {
        System.out.println(session.getRemoteAddress().getHostString() + " connected!");
    }
    @OnWebSocketMessage
    public void onText(String message) {
        System.out.println("Message received:" + message);
        if (session.isOpen()) {
                session.getRemote().sendString(message + " is received.");
        }
    }
    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
        WSystem.out.println(session.getRemoteAddress().getHostString() + " closed!");

    }
}


(8)Java测试 - JavaEE7 WebSocket Example

@ServerEndpoint("/ws/sample")
public class SampleEndpoint {
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("New connection with client: {0}" + session.getId());
    }
    @OnMessage
    public String onMessage(String message, Session session) {
        System.out.println("New message from Client [{0}]: {1}" +  new Object[] {session.getId(), message});
        return "Server received [" + message + "]";
    }
    @OnClose
    public void onClose(Session session) {
        System.out.println("Close connection for client: {0}" +  session.getId());
    }
    @OnError
    public void onError(Throwable exception, Session session) {
       System.out.println("Error for client: {0}" + session.getId());
    }
}


Java WebSocket API : JSR 356


(9)Java测试 - Spring WebSocket Example

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
 
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
 
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
         registry.addEndpoint("/chat").withSockJS();
    }
}

@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
    String time = new SimpleDateFormat("HH:mm").format(new Date());
    return new OutputMessage(message.getFrom(), message.getText(), time);
}


(10)Java测试 - Spring Boot WebSocket Example

pom.xml
<!-- use websocket -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>


@Component
public class EchoHandler extends TextWebSocketHandler {

    private Map<String, WebSocketSession> sessionPool = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        this.sessionPool.put(session.getId(), session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        this.sessionPool.remove(session.getId());
    }

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        for (Entry<String, WebSocketSession> entry : this.sessionPool.entrySet()) {
            entry.getValue().sendMessage(message);
        }
    }
}

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private EchoHandler echoHandler;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(echoHandler, "/echo");
    }
}


(11)第三方API
Sockjs https://github.com/sockjs
STOMP.js https://github.com/jmesnil/stomp-websocket

服务器端:
@Configuration
@EnableWebSocketMessageBroker
public class AppWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/calcApp");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/add").withSockJS();
    }
} 

@Controller
public class WebSocketController {
    @MessageMapping("/add" )
    @SendTo("/topic/showResult")
    public Result addNum(CalcInput input) throws Exception {
        Thread.sleep(2000);
        Result result = new Result(input.getNum1()+"+"+input.getNum2()+"="+(input.getNum1()+input.getNum2())); 
        return result;
    }
}


客户端:
var socket = new SockJS('/Spring4WebSocket/add');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
    console.log('Connected: ' + frame);
    stompClient.subscribe('/topic/showResult', function(calResult){
        console.log(calResult.body);
    });
});


(12)问题
LoadBalancer
Web Filtering

参考:
https://hpbn.co/websocket/
http://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/
https://www.webcodegeeks.com/html5/html5-websocket-example/
http://qiita.com/yuba/items/00fc1892b296fb7b8de9
http://www.slideshare.net/ffdead/the-html5-websocket-api
  • 大小: 18.4 KB
  • 大小: 92.4 KB
  • 大小: 29.9 KB
  • 大小: 14.2 KB
  • 大小: 22.9 KB
  • 大小: 9.3 KB
  • 大小: 26.4 KB
分享到:
评论
1 楼 FengZiIT 2017-01-23  
GoEasy推送,它有Restful API 支持多语言,同时它也支持客户端推送。由于它支持websocket 和polling两种连接方式所以兼顾大多数主流浏览器,低版本的IE浏览器也是支持的,个人觉得很不错。网址:goeasy.io

相关推荐

Global site tag (gtag.js) - Google Analytics