上篇簡單介紹了下爲什麼使用Netty,這篇我們將簡單的搭個HelloWord。
在Netty使用手冊中說了世界上最簡單的協議不是”Hello,World!”,是DISCARD,他是一種丟棄了所有接受到的數據,並不做有任何的響應的協議,本文中就不已拋棄協議爲藍本了,我們就簡單搭個服務端、客戶端。
客戶端發送信息、服務端接收處理並返回給客戶端結果。
整個demo有四個類:
當然netty的maven依賴也要加入:
<!--netty-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
整個準備情況基本完成。
服務器端代碼:
package com.herman.helloWord;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @author hsh
* @create 2018-05-08 18:12
**/
public class Service {
public static final int port = 888;
public static final String ip = "localhost";
public static void main(String[] args) throws InterruptedException {
System.out.println("服務端啓動...");
//1 初始化兩個線程組
//一個是用於處理服務器端接收客戶端連接的
//一個是進行網絡通信的(網絡讀寫的)
NioEventLoopGroup pGroup = new NioEventLoopGroup();
NioEventLoopGroup cGroup = new NioEventLoopGroup();
//2 創建輔助工具類 用於服務器通道的一系列配置
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(pGroup, cGroup)//綁定兩個線程組
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)//設置TCP緩存大小
.option(ChannelOption.SO_SNDBUF, 32 * 1024)//設置發送緩存大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024)//設置接收緩存大小
.option(ChannelOption.SO_KEEPALIVE, true)//保持連接
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//3 配置具體業務處理邏輯(可多個)
socketChannel.pipeline().addLast(new ServerHanler());
}
});
//綁定
ChannelFuture future = bootstrap.bind(port).sync();
//等待關閉
future.channel().closeFuture().sync();
//釋放管道
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
服務端中對應的業務邏輯處理ServerHanler代碼:
package com.herman.helloWord;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* @author hsh
* @create 2018-05-08 18:39
**/
public class ServerHanler extends ChannelHandlerAdapter {
//激活時調用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active...");
}
//讀取
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//從內存中獲取消息引用地址
ByteBuf byteBuf = (ByteBuf) msg;
//生成內存數組
byte[] req = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(req);//copy數據到內存
String body = new String(req, "UTF-8");
System.out.println("Server:" + body);
String response = "服務器返回給客戶端:" + body;
//返回給客戶端
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
}
//讀取完成
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("服務端讀完了");
ctx.flush();
}
//出現異常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
System.out.println("出現了異常");
throw new RuntimeException("出現了錯誤:", cause);
}
}
以上服務端搭建完成,定義了兩個NioEventLoopGroup 並使用輔助工具ServerBootstrap來初始化了一個服務端,並通過childHandler方法定義了處理業務的ServerHanler 。
在ServerHanler 中繼承了ChannelHandlerAdapter 並重寫了其中幾個主要的方法。
客戶端代碼:
package com.herman.helloWord;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
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.NioSocketChannel;
/**
* @author hsh
* @create 2018-05-09 9:25
**/
public class Client {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture future = bootstrap.connect(Service.ip, Service.port).sync();
Thread.sleep(1000);
future.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
future.channel().writeAndFlush(Unpooled.copiedBuffer("666".getBytes()));
Thread.sleep(2000);
future.channel().writeAndFlush(Unpooled.copiedBuffer("888".getBytes()));
future.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
客戶端的業務處理類
package com.herman.helloWord;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
/**
* @author hsh
* @create 2018-05-09 9:27
**/
public class ClientHandler extends ChannelHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channel active...");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] req = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("Client :" + body );
String response="收到服務端返回的信息:"+body;
}finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("客戶端讀完了");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
throw new RuntimeException("客戶端出現了異常:", cause);
}
}
客戶端的代碼基本與服務端一致,主要區別是服務端初始化時指定的是NioServerSocketChannel,客戶端是NioSocketChannel。服務端輔助類是調用bind、客戶端是connect。
業務邏輯是客戶端向服務端發送三次數據,服務端接收之後會返回給客戶端相應,客戶端輸出服務器返回的數據。
啓動先啓動服務端,再啓動客戶端,運行效果如下:
服務端:
客戶端: