如何使用IDEA创建你的第一款Netty程序
本系列主要记录笔者在学习Netty过程中的一些心得体会,其中关于Netty部分的资料主要参考书籍《Netty 实战(Netty In Action)》(Norman Maurer 及Marvin Allen Wolfthal 著 何品 译),在这里感谢作者为我们写了这么一本方便学习的书,对于有需要的读者可以自行去购买正版书籍。
本文主要讲解如何使用IDEA构建Maven项目及详细记录第一款Netty程序的实现过程。
1、使用IDEA搭建项目框架
小白说明:
本文虽包含了IDEA构建项目的详细过程,但不保证涵盖读者需要的所有常用操作,若读者想详细了解IDEA的更多使用方法,可以参看笔者分享的此链接,里面附有IDEA详细使用教程PDF版
链接:https://pan.baidu.com/s/1vnw4x9DjX8gYU5cRFPReyw 提取码:9Pq9
1.1 创建一个空白项目
首先选择Create New Project
选择Empty Project用来存放项目
给项目起一个名字,点击Finish
完成的界面如图,此时我们就成功创建了一个空的项目作为最外层的容器。
接下来我们分析一下要实现的程序,我们的小程序是要实现这样一个功能,有一个服务器和一个客户端,客户端连接服务器并向服务器发送一条消息,接着服务器将从客户端收到的消息在返回给客户端。
之所以做这样一个小程序,主要目的是为了让读者先从简单的实践中感受一下Netty构建应用程序的逻辑,对Netty有一个初步的认识。
从上面的描述中我们很容易想到的是在这个项目里我们需要两个模块,一个服务器一个客户端,所以我们需要构件一个聚合工程,此处笔者在聚合工程之上还创建了一个父工程,意在向读者展示IDEA中如何创建父子工程及父工程同聚合工程的联系与区别。
1.2 创建一个父工程
创建父工程的详细步骤如下所示:
这里我们选择创建一个Maven项目
填写项目的ArtifactId
自定义自己电脑上安装的Maven相关设置
设置模块名字
创建好的工程结构下图:
在这里笔者习惯删除用不到的文件,修改及配置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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ian</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.12</junit.version>
<netty.version>4.1.32.Final</netty.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
至此我们的父工程就创建好了。
1.3 创建一个聚合工程
接下来我们来进入正题创建一个聚合工程,创建过程如下所示:
同样设置一下ArtifactId
小白说明
注意这里笔者将Add as module to 选择为了none,Parent为parent,意思为此工程继承至父工程但和父工程不是聚合关系
和父工程一样设置
设置模块名字创建好的工程如图,同样也删去了不用的文件及配置了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>parent</artifactId>
<groupId>com.ian</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../Parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>manager</artifactId>
<packaging>pom</packaging>
<modules>
<module>Client</module>
<module>Server</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<echo-server.hostname>localhost</echo-server.hostname>
<echo-server.port>9999</echo-server.port>
</properties>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
</project>
此时聚合工程就创建好啦。
1.4 创建服务器子工程
创建服务器子工程的步骤如下:
设置ArtifactId,注意此处Add as module to就不是none了,因为服务器子工程同manager是聚合关系
配置同父工程一样
设置模块名字
创建好的工程如下:
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>manager</artifactId>
<groupId>com.ian</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>server</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>run-server</id>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>nia.echo.EchoServer</mainClass>
<arguments>
<argument>${echo-server.port}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
至此服务器子工程就搭建好啦,最后我们来搭建客户端子工程。
1.5 创建客户端工程
创建客户端工程的步骤如下所示:
和前面类似
设置ArtifactId
配置同父工程一样
设置工程名字
创建好的工程结构如下:
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>manager</artifactId>
<groupId>com.ian</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>client</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>run-client</id>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>nia.echo.EchoClient</mainClass>
<arguments>
<argument>${echo-server.hostname}</argument>
<argument>${echo-server.port}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
至此客户端工程也搭建完成啦,接下来我们就要正式开始写代码实现我们的第一个基于Netty的小程序啦!
2、编写客户端服务器代码
2.1 编写服务器代码
所有Netty服务器都需要以下两部分
1.至少一个ChannelHandler,该组件实现了服务器对从客户端接受的数据的处理,即它的业务逻辑。
2.引导,这是配置服务器的启动代码。至少它会将服务器绑定到它要监听连接请求的端口上。
接下来我们来编写Echo服务器,代码如下所示:
package nia.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
//对于每一个传入的消息都要调用
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
//通知ChannelInboundHandler最后一次对channelRead()的调用时当前批量读取中的最后一条消息
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
//在读取操作期间,有异常抛出时会调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
接下来我们来编写引导服务器,代码如下所示:
package nia.echo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: " + EchoServer.class.getSimpleName() + " <port>");
return;
}
int port = Integer.parseInt(args[0]);
new EchoServer(port).start();
}
public void start() throws Exception {
final EchoServerHandler serverHandler = new EchoServerHandler();
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(serverHandler);
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
}
至此服务器端的代码我们就编写完成啦,接下来编写客户端代码。
2.2 编写客户端代码
客户端主要做如下几个事情:
1.连接到服务器
2.发送一个或者多个消息
3.对于每个消息,等待并接收从服务器发回的相同的消息
4.关闭连接
客户端同样也是业务逻辑和引导两部分,首先我们来编写客户端,代码如下:
package nia.echo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
@Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
//在到服务器的连接已经建立之后将被调用
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8));
}
//当从服务器接收到一条消息时被调用
@Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));
}
//在处理过程中引发异常时被调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
接下来编写引导客户端,代码如下:
package nia.echo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host,int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host,port)).handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: " + EchoClient.class.getSimpleName() + "<host> <port>");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(host,port).start();
}
}
至此代码的编写就全部完成啦。
3、构建和运行代码
要构建客户端和服务器,首先我们将命令行切换到如下目录下,执行mvn clean package 指令
如看到类似下图的界面代表所有的项目都已经成功被构建
接着我们开启两个终端分别切换到Server和Client目录下,在服务器终端执行mvn exec:java指令,成功后如图,然后在客户端终端执行mvn exec:java指令,会看到如下图所示内容,服务器显示收到客户端发送的消息Netty rocks,客户端显示收到服务器返回的同样的信息
这个时候如果关闭服务器终端再次在客户端终端执行mvn exec:java指令会如下图所示,提示连接失败,因为客户端尝试连接服务器运行在localhost:9999上但是服务器已经停止了,所以在客户端触发了我们编写的exceptionCaught方法打印了如下信息。
至此我们的第一个Netty小程序就完成啦。
后面的系列会不定期更新~
写文章不易喜欢就点个赞吧~