前言
Netty是又JBOOS提供的一个java开源框架,是一个基于NIO的客户服务器端编程框架。优势:
1、netty提供简易的API。
2、基于事件驱动的编程方式来编程网络通讯程序。
3、更高的吞吐量。
4、多种编解码功能,支持多种主流协议。
那么今天就基于netty+websocket实现简易的网页聊天的Demo。
一、引入pom文件依赖。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.36.Final</version>
</dependency>
二、主方法
里面首先创建两个线程,主线程池和从线程池。然后创建Netty服务启动对象serverBootstrap。很多都是固定配置,把两个线程加载到group里,然后指定通道类型,初始化加载channel。并且定义端口号。
public class WebSocketNettyService {
public static void main(String[] args) {
NioEventLoopGroup mainGrp = new NioEventLoopGroup(); //主线程池
NioEventLoopGroup minorGrp = new NioEventLoopGroup(); //从线程池
try {
ServerBootstrap serverBootstrap = new ServerBootstrap(); //创建Netty服务器启动对象
serverBootstrap
.group(mainGrp, minorGrp) //指定上面创建的两个线程池
.channel(NioServerSocketChannel.class) //指定通道类型
.childHandler(new WebSocketChannelInitializer()); //指定初始化器用来加载Channel收到事件消息
ChannelFuture future = serverBootstrap.bind(8095).sync(); //服务器绑定端口,启动服务,同步
future.channel().closeFuture().sync(); //等待服务器关闭
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mainGrp.shutdownGracefully();
minorGrp.shutdownGracefully();
}
}
}
三、通道初始化器
通道初始化器,用来加载通道通道处理器,在这个方法加载对应的ChannelHandler。
public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new HttpServerCodec()); //添加一个http的编解码器
pipeline.addLast(new ChunkedWriteHandler()); //添加一个大数据流的支持
pipeline.addLast(new HttpObjectAggregator(1024 * 64)); //添加一个聚合器,主要将HTTPMessage聚合成FullhttpRequest/Response
pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); //需求接收请求的路由,必须使用以ws结尾的请求才能进入
pipeline.addLast(new ChatHandler());//自定义Handler
}
public void handlerAdded(ChannelHandlerContext channelHandlerContext) throws Exception {
}
public void handlerRemoved(ChannelHandlerContext channelHandlerContext) throws Exception {
}
public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable throwable) throws Exception {
}
}
四、自定义的Handler方法
这里首先定义一个静态变量clients,用来保存所有的客户端连接。里面的channelRead0方法是当Channel有新的事件消息会自动调用, 而handlerAdded方法则是当有新的客户端连接服务器会自动调用这个方法。
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd hh:MM");
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame mgs) throws Exception {
String text = mgs.text(); //获取客户端发送过来的文本消息
System.out.println("接收到的消息:" + text);
for(Channel client: clients){
client.writeAndFlush(new TextWebSocketFrame(sdf.format(new Date())+":"+text)); //将消息发送给所有客户端
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
clients.add(ctx.channel());
}
}
五、前端页面
前端页面比较简单,就是建立连接,发送消息和接收消息等。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" id="send_message">
<input type="button" value="发送消息" onclick="sendMessage()">
接收到的消息:
<p id="accept_message" style="background-color:#AAAAAS"></p>
<script>
var websocket = null;
if (window.WebSocket) { //判断当前浏览器是否支持websocket
websocket = new WebSocket("ws://127.0.0.1:9090/ws");
websocket.onopen = function () {
console.log("建立连接");
}
websocket.close = function () {
console.log("断开连接");
}
websocket.onmessage = function (e) {
console.log("接收到服务器的消息:" + e.data)
var accept_message = document.getElementById("accept_message");
accept_message.innerHTML += e.data + "<br/>";
}
} else {
alert("当前浏览器不支持websocket");
}
function sendMessage() {
var send_message = document.getElementById("send_message");
websocket.send(send_message.value);
}
</script>
</body>
</html>
最后运行演示如图: