網頁聊天室效果展示:
① 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