1. 程式人生 > >使用netty實現一個多人聊天室--failed: Error during WebSocket handshake: Unexpected response code: 200

使用netty實現一個多人聊天室--failed: Error during WebSocket handshake: Unexpected response code: 200

初次接觸netty , 本文主要內容如下:

遇到的小bug

在使用netty進行websocket程式設計(實現一個簡單的聊天室)時,我遇到了這樣一個奇怪問題failed: Error during WebSocket handshake: Unexpected response code: 200
google了一下發現還真有人也有這個錯誤。最後發現這是一個很低階的錯誤:
因為我之前寫了一個websocket伺服器埠還沒釋放,於是又寫了一個新的websocket伺服器同時執行,就出現了這個錯誤。


解決方法:關掉idea中所有執行的websocket埠,重新執行程式。
我的伺服器端程式碼如下:

ChannelFuture future= server.bind(8080).sync()

js客戶端程式碼:

//169.254.184.238是我用ipconfig Ping出來的本機ip地址
CHAT.socket =new WebSocket("ws://169.254.184.238:8080/ws");

然後一個簡單的多人聊天室的小bug就解決了

聊天室後端程式碼:

package com.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * netty伺服器端啟動類
 * @author zgm
 * @date 2018/10/4 12:08
 */
public class WSServer {
    public static void main(String[] args) throws  Exception{
        EventLoopGroup mainGroup = new NioEventLoopGroup();
        EventLoopGroup subGroup= new NioEventLoopGroup();

        try {
            ServerBootstrap server=new ServerBootstrap();
            server.group(mainGroup,subGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new WSServerInitializer());

            ChannelFuture future= server.bind(8080).sync();

            future.channel().closeFuture().sync();
        } finally {
            mainGroup.shutdownGracefully();
            subGroup.shutdownGracefully();
        }

    }
}

package com.netty.websocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * 通道初始化
 * @author zgm
 * @date 2018/10/4 12:12
 */
public class WSServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();

        //websocket基於http協議,所以要有http編解碼器
        pipeline.addLast(new HttpServerCodec());
        //對寫大資料流的支援
        pipeline.addLast(new ChunkedWriteHandler());
        //對httpMessage進行聚合,聚合成FullHttpRequest或FullHttpResponse
        //幾乎在netty中的程式設計,都會使用到此handler
        pipeline.addLast(new HttpObjectAggregator(1024 * 64));

        // ======================以上用於支援http協議 ==================================

        /**
         * websocket 伺服器處理的協議,用於指定給客戶端訪問的路由: /ws
         * 本handler會幫你處理一些繁重複雜的事
         * 會幫你處理握手動作 : handshaking (close, ping, pong) ping+pong=心跳
         * 對於websocket來講,都是以frames進行傳輸的,不同的資料型別對應frames不同
         */
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

        //自定義的handler
        pipeline.addLast(new ChatHandler());
    }
}

package com.netty.websocket;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.time.LocalDateTime;

/**
 * 處理訊息的handler
 * TextWebSocketFrame: 在netty中,,是用於為websocket專門處理文字的物件,frame是訊息的載體
 *
 * @author zgm
 * @date 2018/10/4 12:26
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    //用於記錄和管理所有客戶端的channel
    private static ChannelGroup clients= new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        //獲取客戶端傳輸過來的訊息
        String content = textWebSocketFrame.text();
        System.out.println("接受到的資料: " + content);

/*        for(Channel channel : clients){
            channel.writeAndFlush(new TextWebSocketFrame("【伺服器在 " + LocalDateTime.now())+"接收到訊息】 , 訊息為: " + content);
        }*/
        //下面這條語句和上面的for迴圈一樣效果
        clients.writeAndFlush(new TextWebSocketFrame("【伺服器在 " + LocalDateTime.now()+"接收到訊息】 , 訊息為: " + content));
    }

    /**
     * 當客戶端連線伺服器端之後(開啟連線)
     * 獲取客戶端的channel,並放到ChannelGroup中去進行管理
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //當觸發handlerRemoved,ChannelGroup會自動移除客戶端的channel
        //clients.remove(ctx.channel());
        System.out.println("客戶端斷開,channel對應的長id為: "+ ctx.channel().id().asLongText());
        System.out.println("客戶端斷開,channel對應的短id為: "+ctx.channel().id().asShortText());
    }

}

聊天室前端程式碼:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>hello</title>
	</head>
	<body>
		<div>傳送訊息</div>
		<input type="text" id="msgContent" />
		<input type="button" value="點我傳送" onclick="CHAT.chat()" />
		<div>接受訊息</div>
		<div id= "receiveMsg" style="background-color:orange;"></div>
		
		
		<script type="application/javascript">
			
			window.CHAT= {
				socket: null,
				init:function(){
					if (window.WebSocket){
						CHAT.socket =new WebSocket("ws://169.254.184.238:8080/ws");
						CHAT.socket.onopen=function(){
							console.log("連線建立成功");
						},
						CHAT.socket.onclose=function(){
							console.log("連線關閉");
						},
						CHAT.socket.onerror=function(){
							console.log("發生錯誤");
						},
						CHAT.socket.onmessage=function(e){
							console.log("接受到訊息:"+ e.data);
						    var receiveMsg= document.getElementById("receiveMsg");
						    var html = receiveMsg.innerHTML ;
						    receiveMsg.innerHTML=html+"<br/>"+e.data;
						}
				
					} else{
						alert("瀏覽器不支援websocket協議。。。");
					}
				},
				chat:function(){
					var msg =document.getElementById("msgContent");
					CHAT.socket.send(msg.value);
				}
			};
			CHAT.init();
		</script>
		
	</body>
</html>

以上程式碼就實現了一個簡單的聊天室。