【Netty】简单构建服务端&客户端(web/java)
创建服务器端 Server
Server
public class Server {
public static void main(String[] args) {
//bossGroup负责处理连接请求,WorkerGroup负责处理业务请求。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//自定义处理器
pipeline.addLast(new ServerHandler());
//当客户端为Web端时需要添加以下处理
//websocket基于http协议,添加http的编解码器
pipeline.addLast(new HttpServerCodec());
//对http中大数据的数据流,提供写大数据流的支持
pipeline.addLast(new ChunkedWriteHandler());
//对httpMessage进行聚合处理,聚合成FullHttpRequest或FullHttpResponse
pipeline.addLast(new HttpObjectAggregator(1024*64));
/*
* 处理一些繁重复杂的事情;
* 处理websocket的握手动作:handshaking(close/ping/pong) ping+pong=心跳
从Http协议升级到Websocket协议,是通过StatusCode 101(Switching Protocols)来切换的。
* 对于websocket来说,都是以frames进行传输,不同的数据类型frames不同
* 并且要指定与客户端交互的路径:ws://localhost:8088/chat
*/
pipeline.addLast(new WebSocketServerProtocolHandler("/chat"));
}
});
System.out.println("服务已启动");
//监听端口
ChannelFuture channelFuture = bootstrap.bind(8888).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
自定义处理器 ServerHandler
//java客户端
//public class ServerHandler extends ChannelInboundHandlerAdapter{}
/**
* 由于它的传输数据载体是frame,这个frame在netty中,是用于websocket专门处理文本对象的
* netty中用TextWebSocketFrame对象来封装frame
*/
public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
//客户端组
private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
// @Override
// public void channelRead(ChannelHandlerContext ctx,Object msg){
// ByteBuf byteBuf = (ByteBuf) msg;
// System.out.println("收到客户端消息:" + ctx.channel().remoteAddress() + ":" + byteBuf.toString(CharsetUtil.UTF_8));
// String data = byteBuf.toString(CharsetUtil.UTF_8);
// clients.writeAndFlush(new TextWebSocketFrame("[来自桌面客户端的消息" + new Date() + " ] : " + data));
// }
//收到客户端消息
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg){
String text = msg.text();
System.out.println("收到客户端消息:" + ctx.channel().remoteAddress() + ":" + text);
clients.writeAndFlush(new TextWebSocketFrame("【" + ctx.channel().remoteAddress().toString() + "】:" + text));
}
/**
* 服务器接收到客户端的消息后,给客户端返回消息
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx){
// ctx.writeAndFlush(Unpooled.copiedBuffer("收到了客户端的消息",CharsetUtil.UTF_8));
}
//客户端加入
@Override
public void handlerAdded(ChannelHandlerContext ctx){
System.out.println("有新客户端连接进入:" + ctx.channel().remoteAddress());
clients.add(ctx.channel());
}
//出现异常,可加入日志
@Override
public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
ctx.close();
}
//客户端离开
@Override
public void handlerRemoved(ChannelHandlerContext ctx){
System.out.println("有客户端离开:" + ctx.channel().remoteAddress());
}
}
Web 客户端
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Netty通信</title>
</head>
<body>
发送消息:<input type="text" id="msgContent"/>
<input type="button" value="发送消息" onclick="CHAT.chat()" />
<hr />
接受消息:
<div id="receiveMsg"></div>
<script type="text/javascript">
window.CHAT = {
socket:null,
//初始化socket
init:function(){
//判断浏览器是否支持websocket
if(window.WebSocket){
//创建websocket对象
CHAT.socket = new WebSocket("ws://127.0.0.1:8888/chat");
CHAT.socket.onopen = function(){
console.log("连接建立成功");
},
CHAT.socket.close = 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{
console.log("浏览器不支持websocket协议");
}
},
//发送消息时触发的函数
chat:function(){
//获取消息框中所输入的内容
var msgContent = document.getElementById("msgContent").value;
//将客户端输入的消息进行发送
CHAT.socket.send(msgContent);
}
};
CHAT.init();
</script>
</body>
</html>
java 客户端
客户端启动
public class Client {
public static void main(String[] args) {
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//自定义处理器
pipeline.addLast(new ClientHandler());
}
});
System.out.println("客户端已启动");
//连接服务器
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1",8888).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
//销毁
eventExecutors.shutdownGracefully();
}
}
}
客户端自定义处理器
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx){
String substring = UUID.randomUUID().toString().replace("-", "").substring(5, 10);
ctx.writeAndFlush(Unpooled.copiedBuffer("你好服务器,我是客户端" + substring, CharsetUtil.UTF_8));
}
@Override
public void channelRead(ChannelHandlerContext ctx,Object msg){
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("收到服务器的消息:" + ctx.channel().remoteAddress() + ": " + byteBuf.toString(CharsetUtil.UTF_8));
}
}