遇到的小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>
以上代碼就實現了一個簡單的聊天室。