使用netty實現一個多人聊天室--failed: Error during WebSocket handshake: Unexpected response code: 200
阿新 • • 發佈:2018-12-24
初次接觸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>
以上程式碼就實現了一個簡單的聊天室。