Netty4 + WebSocket 實現網頁版聊天室

網頁聊天室效果展示:

 

 

① Netty依賴

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.32.Final</version>
</dependency>

 

② Netty的服務端:定義NettyServer,讓其支持Websocket通信,並添加自己的請求處理器

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;

public class NettyServer {

	private int port;

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

	public void start() {

		// 創建服務類
		ServerBootstrap sb = new ServerBootstrap();

		// 創建boss和woker
		NioEventLoopGroup boss = new NioEventLoopGroup();
		NioEventLoopGroup woker = new NioEventLoopGroup();

		try {
			// 設置線程池
			sb.group(boss, woker);

			// 設置channel工廠
			sb.channel(NioServerSocketChannel.class);

			// 設置管道
			sb.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					
					ch.pipeline().addLast(new HttpServerCodec());// websocket基於http協議,需要HttpServerCodec
					ch.pipeline().addLast(new HttpObjectAggregator(65535)); // http消息組裝
					ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws")); // websocket通信支持
					ch.pipeline().addLast(new MyServerHandler()); // 自定義處理器
					
				}
			});

			// 服務器異步創建綁定
			ChannelFuture cf = sb.bind(port).sync();

			// 等待服務端關閉
			cf.channel().closeFuture().sync();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			boss.shutdownGracefully();
			woker.shutdownGracefully();
		}
	}

	/**
	 * server啓動
	 * @param args
	 */
	public static void main(String[] args) {
		new NettyServer(1234).start();
	}
}

③ 自定義監聽Channel的處理器

import io.netty.channel.Channel;
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;

public class MyServerHandler extends
		SimpleChannelInboundHandler<TextWebSocketFrame> {

	public static ChannelGroup channels = new DefaultChannelGroup(
			GlobalEventExecutor.INSTANCE);

	/**
	 * 監聽客戶端註冊
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		// 新客戶端連接,通知其他客戶端
		for (Channel channel : channels) {
			String msg = "<font color='red'>~~用戶"
					+ ctx.channel().remoteAddress() + "上線~~<font>";
			channel.writeAndFlush(msgPot(msg));
		}
		// 加入隊列
		channels.add(ctx.channel());
	}

	/**
	 * 監聽客戶端斷開
	 */
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		// 離開時,通知其他客戶端
		for (Channel channel : channels) {
			if (channel != ctx.channel()) {
				String msg = "<font color='red'>~~用戶"
						+ ctx.channel().remoteAddress() + "離開~~<font>";
				channel.writeAndFlush(msgPot(msg));
			}
		}
		// 整理隊列
		channels.remove(ctx.channel());
	}

	/**
	 * 讀取客戶端發過來的消息
	 */
	@Override
	public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame tmsg)
			throws Exception {
		// 處理消息
		for (Channel channel : channels) {
			// 判斷是否是當前用戶的消息
			if (channel != ctx.channel()) {
				String msg = "[用戶" + channel.remoteAddress() + " 說:]"
						+ tmsg.text();
				channel.writeAndFlush(msgPot(msg));
			} else {
				String msg = "[我說:]" + tmsg.text();
				channel.writeAndFlush(msgPot(msg));
			}
		}
	}

	/**
	 * 監聽連接異常
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		ctx.close(); // 關閉
	}

	/**
	 * 封裝消息
	 * 
	 * @param msg
	 * @return
	 */
	public TextWebSocketFrame msgPot(String msg) {
		return new TextWebSocketFrame(msg);
	}

}

 

至此,服務端的代碼就已經結束了,下面開始書寫頁面客戶端代碼

 

④ netty.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>netty聊天室</title>
<style>
.webchat {
	position: absolute;
	top: 15%;
	left: 35%;
	text-align: center;
}

#content {
	width: 345px;
	height: 345px;
	border-left: 1px solid #AAAAAA;
	border-right: 1px solid #AAAAAA;
	border-top: 2px solid #AAAAAA;
	border-bottom: 2px solid #AAAAAA;
	border-radius: 2px;
	margin: 9px 0;
	overflow: auto;
}

.button {
	background: #60F49B;
	color: #ffffff;
	border: none;
	height: 30px;
}

#msg {
	height: 25px;
}
</style>

<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
	<div class="webchat">
		<div class="title">Netty4+Websocket聊天室</div>
		<div id="content"></div>
		<input type="text" id="msg" /> <input class="button" type="button"
			value="發送消息" onclick="CHAT.chat()" /> <input class="button"
			type="button" value="清空聊天記錄" onclick="CHAT.clean()" />
	</div>
</body>

<script type="text/javascript">
	window.CHAT = {
		socket : null,
		init : function() {
			if (window.WebSocket) {
				// ws://機器地址:netty綁定的端口/服務端定義socket路徑
				CHAT.socket = new WebSocket("ws://192.168.1.126:1234/ws");

				CHAT.socket.onopen = function() {
					console.log("連接成功");
				}, CHAT.socket.onclose = function() {
					console.log("連接關閉");
				}, CHAT.socket.onerror = function() {
					console.log("異常");
				}, CHAT.socket.onmessage = function(e) {
					var htm = $("#content").html();
					$("#content").html(htm + "<br>" + e.data)
				}

			} else {
				alert("瀏覽器不支持websocket協議.....");
			}
		},
		chat : function() {
			if ($("#msg").val() != "")
				CHAT.socket.send($("#msg").val());
		},
		clean : function() {
			$("#content").html("")
		}
	}
	CHAT.init();
</script>
</html>

 

tip:web項目和Netty服務的啓動問題

① 先執行NettyServer的main方法,開啓NettyServer。然後tomcat啓動web項目,訪問netty.jsp。

② web項目中啓動時創建一個線程去啓動Netty服務,訪問netty.jsp

 

參考博客:https://www.jianshu.com/p/0b2772f5778c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章