netty與websocket通信demo

jopen 10年前發布 | 79K 次閱讀 WebSocket 開發 WebSocket

netty v3.9.4

websocket連接建立前,客戶端需要與服務器進行握手(http協議) 確認websocket連接,也就是說在處理websocket請求前,必需要處理一些http請求。

websocket到現在為止,已經有多個版本,netty有相應的對應類,這部分處理一般不需要人工干預。

 

如果運行正常的話,會在頁面的文本框中顯示1-20記數。

可以通過firefox或chrome的開發人員工具,顯看瀏覽器與服務器的交互。

 

主要是HttpServerChannelHandler2,加了些注釋和少量debug代碼。

package org.sl.demo.httpserver1;

import java.util.List;
import java.util.Map;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;

public class HttpServerChannelHandler2 extends SimpleChannelHandler{
    public static boolean debug = true;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object msg = e.getMessage();

        if(debug){
            System.out.println("---------------");
            System.out.println("message: "+msg.getClass());
        }
        //雖然是websocket,但在建立websocket連接前,先進行http握手,所以,這時也要處理http請求
        //在http握手完成后,才是websocket下的通信
        if(msg instanceof HttpRequest){
            processHttpRequest(ch, (HttpRequest)msg);
        }else if(msg instanceof WebSocketFrame){
            processWebsocketRequest(ch,(WebSocketFrame)msg);
        }else{
            //未處理的請求類型
        }
    }

    //這個方法:
    //1.完成websocket前的http握手
    //2.屏蔽掉非websocket握手請求
    void processHttpRequest(Channel channel,HttpRequest request){
        HttpHeaders headers = request.headers();
        if(debug){
            List<Map.Entry<String,String>> ls = headers.entries();
            for(Map.Entry<String,String> i: ls){
                System.out.println("header  "+i.getKey()+":"+i.getValue());
            }
        }       

        //屏蔽掉非websocket握手請求
        //只接受http GET和headers['Upgrade']為'websocket'的http請求
        if(!HttpMethod.GET.equals(request.getMethod())
                || !"websocket".equalsIgnoreCase(headers.get("Upgrade"))){
            DefaultHttpResponse resp = new DefaultHttpResponse(
                    HttpVersion.HTTP_1_1,
                    HttpResponseStatus.BAD_REQUEST);
            channel.write(resp);            
            channel.close();
            return;
        }

        WebSocketServerHandshakerFactory wsShakerFactory = new WebSocketServerHandshakerFactory(
                "ws://"+request.headers().get(HttpHeaders.Names.HOST),
                null,false );
        WebSocketServerHandshaker wsShakerHandler = wsShakerFactory.newHandshaker(request);
        if(null==wsShakerHandler){
            //無法處理的websocket版本
            wsShakerFactory.sendUnsupportedWebSocketVersionResponse(channel);
        }else{
            //向客戶端發送websocket握手,完成握手
            //客戶端收到的狀態是101 sitching protocol
            wsShakerHandler.handshake(channel, request);
        }       
    }

    //websocket通信
    void processWebsocketRequest(Channel channel, WebSocketFrame request){      
        if(request instanceof CloseWebSocketFrame){
            channel.close();
        }else if(request instanceof PingWebSocketFrame){         
            channel.write(new PongWebSocketFrame(request.getBinaryData()));  
        }else if(request instanceof TextWebSocketFrame){
            //這個地方 可以根據需求,加上一些業務邏輯
            TextWebSocketFrame txtReq = (TextWebSocketFrame) request;
            if(debug){ System.out.println("txtReq:"+txtReq.getText());}

            //向ws客戶端發送多個響應
            for(int i=1; i<=20; i++){
                channel.write(new TextWebSocketFrame(""+i));
                try{Thread.sleep(300);}catch(Exception ex){}
            }           
        }else{
            //WebSocketFrame還有一些
        }
    }
}

其他類跟網上的差不多: http://blog.csdn.net/shagoo/article/details/8028813

package org.sl.demo.httpserver1;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

public class HttpServer implements Runnable{
    int port = 80;

    public HttpServer(int port){
        this.port = port;
    }

    @Override
    public void run() {
        ServerBootstrap b = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));
        b.setPipelineFactory(new HttpServerChannelPipelineFactory());
        b.setOption("child.tcpNoDelay", true);  
        b.setOption("child.keepAlive", true);
        b.bind(new InetSocketAddress(port));
    }

    public static void main(String[] args){
        new HttpServer(80).run();
    }
}

package org.sl.demo.httpserver1;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.codec.http.websocketx.WebSocket00FrameDecoder;
import org.jboss.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder;

public class HttpServerChannelPipelineFactory  implements ChannelPipelineFactory {

    @Override
    public ChannelPipeline getPipeline() throws Exception {
        ChannelPipeline cp = Channels.pipeline();

        cp.addLast("decoder", new HttpRequestDecoder());
//      cp.addLast("decoder", new WebSocket00FrameDecoder());
        cp.addLast("encoder", new HttpResponseEncoder());
//      cp.addLast("downhandler", new HttpServerDownstreamHandler());
//      cp.addLast("uphandler", new HttpServerUpstreamHandler());
        cp.addLast("handler", new HttpServerChannelHandler2());

        return cp;
    }

}

 

測試頁面:

<html>
<head>
<script >
function connect1(){
    alert('connect1');

    var ta = document.getElementById('responseText');
    var socket = new WebSocket('ws://127.0.0.1/websocket'); 
    if (window.WebSocket) {

    }else{
        alert('Your browser does not support Web Socket.');
        return;
    }
    socket.onopen = function(event) { 
        ta.value = "Web Socket opened!";
    }

    socket.onmessage = function(event) { 
        ta.value = event.data;
    }; 

    socket.onclose = function(event) { 
        ta.value = "Web Socket closed"; 
    }; 
}

function connect() {
    alert('connect');
    var socket;
    if (!window.WebSocket) {
          window.WebSocket = window.MozWebSocket;
    }

    if (window.WebSocket) {
          socket = new WebSocket("ws://127.0.0.1/websocket");
          socket.onmessage = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = event.data;
          };
  
          socket.onopen = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = "Web Socket opened!";
            socket.send('hello');
          };
      
          socket.onclose = function(event) {
            var ta = document.getElementById('responseText');
            ta.value = "Web Socket closed"; 
          };

        socket.onerror = function(event){         
        };  
   } else {
       alert("Your browser does not support Web Socket.");
    }
}
</script>
</head>

<body>
<input type="button" onclick="connect1()" value="ws connect1"/> <br/><br/>
<input type="button" onclick="connect()" value="ws connect"/> <br/><br/>
<input id="responseText" type="text" value="123456"/>
</body>
</html>

 來自:http://my.oschina.net/tangcoffee/blog/338562

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!