Netty是一款異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架坤检,支持快速地開發(fā)可維護(hù)的高性能的面向協(xié)議的服務(wù)器和客戶端稀蟋。 ----摘自《Netty in action》
如果對(duì)于Java網(wǎng)絡(luò)編程不是很熟悉的同學(xué),在第一遍看書的時(shí)候苛白,心里肯定是一個(gè)大寫的懵 - -娃豹,所以老老實(shí)實(shí)的照著教程寫了第一個(gè)netty的客戶端和服務(wù)端,并思考了netty的核心組件购裙。
netty的核心組件:
- Channel (渠道懂版,出入站數(shù)據(jù)的載體)
- 回調(diào)
- Future
- 事件和ChannelHandler
新建項(xiàng)目
- 通過(guò)file -> new -> project -> maven
新建一個(gè)maven項(xiàng)目,不用選擇原型躏率,填寫GroupId和ArtifactId定续,版本號(hào)默認(rèn)。
-
通過(guò)file -> new -> Module 新建客戶端模塊
GroupId和version是默認(rèn)的禾锤,artifactId可以自定義私股。
客戶端模塊 同樣的方式建立服務(wù)端模塊
- 項(xiàng)目結(jié)構(gòu)如下
添加依賴與maven插件
- 主pom.xml文件(最外部的)
這里加了兩個(gè)插件,一個(gè)是maven-compiler-plugin恩掷,主要是用在項(xiàng)目編譯倡鲸、打包的時(shí)候。一個(gè)是exe-maven-plugin黄娘,主要是用來(lái)執(zhí)行main函數(shù)峭状。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.netty</groupId>
<artifactId>nettyJoin</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>nettyClient</module>
<module>nettyServer</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.13.Final</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<!-- put your configurations here -->
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- nettyClient的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nettyJoin</artifactId>
<groupId>com.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.netty</groupId>
<artifactId>nettyClient</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
</project>
- nettyServer的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nettyJoin</artifactId>
<groupId>com.netty</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.netty</groupId>
<artifactId>nettyServer</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
</project>
注:jdk、maven什么的環(huán)境變量這里就不累贅了
編寫客戶端代碼
注:代碼都是參考《netty in action》中的例子
- 處理器EchoClientHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext,
ByteBuf byteBuf) throws Exception {
//記錄已接收消息的轉(zhuǎn)儲(chǔ)
System.out.println("Client received: " + byteBuf.toString(CharsetUtil.UTF_8));
}
/**
* 作為一個(gè)回調(diào)函數(shù)
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//當(dāng)被通知Channel是活躍的時(shí)候逼争,發(fā)送一條消息
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
- main函數(shù)
public class EchoClient {
private int port;
private String host;
public EchoClient(int port, String host) {
this.port = port;
this.host = host;
}
public void start() throws InterruptedException {
//進(jìn)行事件處理分配优床,包括創(chuàng)建新的連接以及處理入站和出站數(shù)據(jù)
EventLoopGroup group = new NioEventLoopGroup();
try {
//創(chuàng)建Bootstrap 初始化客戶端
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel)
throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//連接到遠(yuǎn)程節(jié)點(diǎn),阻塞等待直到連接完成
ChannelFuture f = b.connect().sync();
//阻塞誓焦,直到channel關(guān)閉
f.channel().closeFuture().sync();
} finally {
//關(guān)閉線程池并且釋放所有的資源
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
if (args.length != 2) {
System.err.println("Usages: " + EchoClient.class.getSimpleName() + "<host><port>");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(port, host).start();
}
}
編寫服務(wù)端代碼
- 處理器EchoServerHandler
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
private Logger logger = Logger.getLogger("com.EchoServerHandler");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
//將接收到的消息寫給發(fā)送者胆敞,而不沖刷出站消息
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//將未決消息沖刷到遠(yuǎn)程節(jié)點(diǎn),并且關(guān)閉該channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
- main函數(shù)
public class EchoServer {
private int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
if (args.length != 1) {
System.err.println("Usages: " + EchoServer.class.getSimpleName() + "<port>");
}
int port = Integer.parseInt(args[0]);
new EchoServer(port).start();
}
/**
* 啟動(dòng)方法
*
* @throws InterruptedException
*/
public void start() throws InterruptedException {
final EchoServerHandler serverHandler = new EchoServerHandler();
//1創(chuàng)建EventLoopGroup 用來(lái)接收和處理新的連接
EventLoopGroup group = new NioEventLoopGroup();
try {
//2 創(chuàng)建ServerBootstrap
ServerBootstrap b = new ServerBootstrap();
b.group(group)
//3 制定所使用的NIO傳輸channel
.channel(NioServerSocketChannel.class)
//4 使用制定的端口設(shè)置套接字地址
.localAddress(new InetSocketAddress((port)))
//5 添加一個(gè)EchoServerHandler到子channel的ChannelPipeLine
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel)
throws Exception {
//EchoServerHandler被標(biāo)注為@Shareable杂伟,所以我們可以總是使用同樣的實(shí)例
socketChannel.pipeline().addLast(serverHandler);
}
});
//6 異地綁定服務(wù)器移层;調(diào)用sync方法阻塞等待直到綁定完成
ChannelFuture f = b.bind().sync();
//7 獲取channel的closeFuture,并且阻塞當(dāng)前線程直到它完成
f.channel().closeFuture().sync();
} finally {
//8 關(guān)閉EventLoopGroup釋放所有的資源
group.shutdownGracefully().sync();
}
}
}
目錄結(jié)構(gòu)
運(yùn)行項(xiàng)目
現(xiàn)在本地將客戶端和服務(wù)端的代碼打包赫粥,進(jìn)入兩個(gè)模塊的根目錄观话,運(yùn)行mvn clean package即可。
-
項(xiàng)目打包
客戶端打包
- 運(yùn)行服務(wù)端代碼
進(jìn)入服務(wù)端模塊的根目錄越平,運(yùn)行下面的命令频蛔,后面跟的是main函數(shù)和入?yún)?/li>
mvn exec:java -Dexec.mainClass="com.EchoServer" -Dexec.args="9999"
服務(wù)器啟動(dòng)完畢灵迫,并等待連接。
- 啟動(dòng)客戶端
進(jìn)入客戶端模塊的根目錄晦溪,運(yùn)行下面的命令龟再,后面跟的是main函數(shù)和入?yún)?/li>
mvn exec:java -Dexec.mainClass="com.EchoClient" -Dexec.args="127.0.0.1 9999"
圖中右邊的是客戶端命令行,可以看到已經(jīng)打印出“Client received: Netty rocks!”尼变,服務(wù)端也正常接收到消息利凑,并打印出了“Server received: Netty rocks!”。
小結(jié)
可能在代碼編寫過(guò)程中嫌术,或是編譯打包的過(guò)程中哀澈,都會(huì)碰到各種錯(cuò)誤,因?yàn)榇蠹业倪\(yùn)行環(huán)境都各不相同度气,碰到問(wèn)題的同學(xué)可以在下面留言割按,也可以自行百度。
這也是我第一次接觸netty磷籍,才看了書的前兩章适荣,接下來(lái)也會(huì)把自己的總結(jié)、感悟?qū)懺趎etty上院领。