Netty实现在线聊天

netty

Netty作为当前最流行的网络通讯框架。从一个最简单的实例去了解他无非是最好的方式。

在这里,使用最常见的IM消息的方式去熟悉和使用Netty框架。

在这里我们需要定义三个类,第一个就是启动类(Server),然后就是配置初始化类(Initializer),然后是处理响应(Handler)的类。

首先我们在pom文件中引入我们需要的netty包

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

然后创建我们的启动类WebSocketServer:

public class WebSocketServer {
	public static void main(String[] args) throws Exception {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.childHandler(new WebSocketServerInitializer()).option(ChannelOption.SO_BACKLOG, 128)
					.childOption(ChannelOption.SO_KEEPALIVE, true);

			System.out.println("WebsocketChatServer 启动了  来自LLL丶blog");
			// 绑定端口,开始接收进来的连接
			ChannelFuture f = b.bind(8080).sync();
			// 让服务器不会立马关闭
			f.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
			System.out.println("WebsocketChatServer 关闭了 来自LLL丶blog");
		}
	}
}

然后需要建立我们的初始化类WebSocketServerInitializer,在这里必须继承至ChannelInitializer类,并传入SocketChannel接口:

public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
	@Override
	public void initChannel(SocketChannel ch) throws Exception {
		// 管道配置
		ChannelPipeline pipeline = ch.pipeline();
		pipeline.addLast(new HttpServerCodec());
		pipeline.addLast(new HttpObjectAggregator(64 * 1024));
		pipeline.addLast(new ChunkedWriteHandler());
		pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
		pipeline.addLast(new TextWebSocketFrameHandler());

	}
}

然后创建具体的处理程序类TextWebSocketFrameHandler并且继承至SimpleChannelInboundHandler类:

public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

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

	// 处理从客户端发来的消息,核心方法
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
		Channel incoming = ctx.channel();
		for (Channel channel : channels) {
			if (channel != incoming) {
				channel.writeAndFlush(new TextWebSocketFrame("[" + incoming.remoteAddress() + "]" + msg.text()));
			} else {
				channel.writeAndFlush(new TextWebSocketFrame("[本地发送]:" + msg.text()));
				// Console打印,可以删除
				StringBuffer sb = new StringBuffer();
				sb.append(incoming.remoteAddress()).append("->").append(msg.text());
				System.out.println(sb.toString());
			}
		}
	}

	// 有新通道加入的时候响应
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		Channel incoming = ctx.channel();
		for (Channel channel : channels) {
			channel.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入"));
		}
		channels.add(ctx.channel());
		System.out.println("Client:" + incoming.remoteAddress() + "加入");
	}

	// 有通道关闭时响应
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		Channel incoming = ctx.channel();
		for (Channel channel : channels) {
			channel.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开"));
		}
		System.out.println("Client:" + incoming.remoteAddress() + "离开");
		channels.remove(ctx.channel());
	}

	// 通道激活时响应
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		Channel incoming = ctx.channel();
		System.out.println("Client:" + incoming.remoteAddress() + "在线");
	}

	// 通道关闭时响应
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		Channel incoming = ctx.channel();
		System.out.println("Client:" + incoming.remoteAddress() + "掉线");
	}

	// 异常时响应
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		Channel incoming = ctx.channel();
		System.out.println("Client:" + incoming.remoteAddress() + "异常");

		// 当出现异常就关闭连接
		cause.printStackTrace();
		ctx.close();
	}
}

到这里服务端就创建完毕了,基本上在实际中,我们只需要更改Handler中的代码做出不一样的响应就可以完成其他的一些操作。启动服务端可以看到控制台如图所示,表示成功。 console

下面我们只需要在本地建一个html文件去连接到我们的服务端就好了

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Netty-WebSocket聊天</title>
</head>
<body>
    <script type="text/javascript">
        var socket;
        
        function connectServer() {
         if (!window.WebSocket) {
             window.WebSocket = window.MozWebSocket;
         }
         if (window.WebSocket) {
             socket = new WebSocket("ws://localhost:8080/ws");
             socket.onmessage = function(event) {
                 var ta = document.getElementById('responseText');
                 ta.value = ta.value + '\n' + event.data
             };
             socket.onopen = function(event) {
                 var ta = document.getElementById('responseText');
                 ta.value = "连接开启!";
             };
             socket.onclose = function(event) {
                 var ta = document.getElementById('responseText');
                 ta.value = ta.value + "连接被关闭";
             };
         } else {
             alert("你的浏览器不支持 WebSocket!");
         }
        }
        
        function send(message) {
            if (!window.WebSocket) {
                return;
            }
            if (socket.readyState == WebSocket.OPEN) {
                socket.send(message);
            } else {
                alert("连接没有开启.");
            }
        }
    </script>
    <form onsubmit="return false;">
     <input type="button" onclick="javascript:connectServer()" value="连接服务器">
        <h3>Netty-码家学院 聊天室:</h3>
        <textarea id="responseText" style="width: 500px; height: 300px;"></textarea>
        <br> 
        <input type="text" name="message"  style="width: 300px" value="聊天文字在此....">
        <input type="button" value="发送消息" onclick="send(this.form.message.value)">
        <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录">
    </form>
    <br> 
    <br> 
</body>
</html>

然后用两个不同的浏览器打开,点击左上角连接到服务器,就可以看到自己连接到服务器成功了。 在聊天中可以看到两个浏览器窗口都能同时输出信息,如图所示 输入图片说明输入图片说明

同样在服务端的控制台可以看到输出信息 输入图片说明

有了这个简单的示例之后,简单改造改造就可以形成一个简单的聊天工具了

Netty的强大之处远远不止这些。需要自己去探讨,这里只是做个简单的示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SoXiaTea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值