NIO服务端与客户端代码
package NIO高级编程与Netty入门10;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
//NIO客户端
public class NIOClientAndServer {
public static void main(String[] args) throws IOException {
System.out.println("客户端已经启动....");
// 1.创建通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));//打开套接字通道并将其连接到远程地址。
// 2.切换异步非阻塞
sChannel.configureBlocking(false);//继承自AbstractSelectableChannel,调整此通道的阻塞模式。
// 3.指定缓冲区大小
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// Scanner scanner= new Scanner(System.in);
// while (scanner.hasNext()) {
// String str=scanner.next();
// byteBuffer.put((new Date().toString()+"\n"+str).getBytes());
byteBuffer.put(("你好NIO服务端,我给你传了现在的时间》》》"+new Date().toString()).getBytes());
// 4.切换读取模式
byteBuffer.flip();
sChannel.write(byteBuffer);
byteBuffer.clear();
// }
sChannel.close();
}
}
class NIOServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器端已经启动....");
// 1.创建通道
ServerSocketChannel sChannel = ServerSocketChannel.open();//打开服务器套接字通道
// 2.切换读取模式
sChannel.configureBlocking(false);//继承自AbstractSelectableChannel,调整此通道的阻塞模式。
// 3.绑定连接
sChannel.bind(new InetSocketAddress(8080));//套接字通道绑定端口
// 4.获取选择器
Selector selector = Selector.open();//获取一个选择器
// 5.将通道注册到选择器 "并且指定监听接受事件"
sChannel.register(selector, SelectionKey.OP_ACCEPT);//将通道注册到选择器 "并且指定监听接受事件"
// 6. 轮训式 获取选择 "已经准备就绪"的事件
int a=0;
while ((a=selector.select()) > 0) {
// System.out.println("a="+a);//每接收一次请求,这里的a+1
// 7.获取当前选择器所有注册的"选择键(已经就绪的监听事件)"
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
// 8.获取准备就绪的事件
SelectionKey sk = it.next();
// 9.判断具体是什么事件准备就绪
if (sk.isAcceptable()) {
// 10.若"接受就绪",获取客户端连接
SocketChannel socketChannel = sChannel.accept();
// 11.设置阻塞模式
socketChannel.configureBlocking(false);
// 12.将该通道注册到服务器上
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {//可读就绪
// 13.获取当前选择器"就绪" 状态的通道
SocketChannel socketChannel = (SocketChannel) sk.channel();
// 14.读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);
int len = 0;
while ((len = socketChannel.read(buf)) > 0) {
buf.flip();
System.out.println("NIO客户端说:"+new String(buf.array(), 0, len));
buf.clear();
}
}
it.remove();
}
}
}
}
选择KEY
1、SelectionKey.OP_CONNECT
2、SelectionKey.OP_ACCEPT
3、SelectionKey.OP_READ
4、SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey类的源码中我们可以看到如下的4中属性,四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接
Netty快速入门
什么是Netty
Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。
Netty应用场景
1.分布式开源框架中dubbo、Zookeeper,RocketMQ底层rpc通讯使用就是netty。
2.游戏开发中,底层使用netty通讯。
为什么选择netty
在本小节,我们总结下为什么不建议开发者直接使用JDK的NIO类库进行开发的原因:
1、NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;
2、需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;
3、可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大;
4、JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决。
Netty坐标
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.3.0.Final</version>
</dependency>
nettyserver代码
package NIO高级编程与Netty入门10;
import java.net.InetSocketAddress;
import java.nio.channels.Channels;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
class ServerHanlder extends SimpleChannelHandler {
// 通道被关闭的时候会触发
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelClosed(ctx, e);
System.out.println("channelClosed");
}
// 必须要建立连接,关闭通道的时候才会触发
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelDisconnected(ctx, e);
System.out.println("channelDisconnected");
}
// 接受出现异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
super.exceptionCaught(ctx, e);
System.out.println("exceptionCaught");
}
// 接受客户端数据..
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
super.messageReceived(ctx, e);
System.out.println("messageReceived");
System.out.println("服务器获取客户端发来的参数:" + e.getMessage());
ctx.getChannel().write("你好啊!");
}
}
// netty 服务器端
public class NettyServer {
public static void main(String[] args) {
// 1.创建服务对象
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 2.创建两个线程池 第一个 监听端口号 nio监听
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService wook = Executors.newCachedThreadPool();
// 3.将线程池放入工程
serverBootstrap.setFactory(new NioServerSocketChannelFactory(boos, wook));
// 4.设置管道工程
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
// 设置管道
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline();
// 传输数据的时候直接为string类型
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 设置事件监听类
pipeline.addLast("serverHanlder", new ServerHanlder());
return pipeline;
}
});
// 绑定端口号
serverBootstrap.bind(new InetSocketAddress(8080));
System.out.println("服务器端已经被启动.....");
// while (true) {
// try {
// Thread.sleep(500);
// } catch (Exception e) {
// // TODO: handle exception
// }
// System.out.println("每隔0.五秒打印.....");
//
// }
}
}
nettyClient代码
package NIO高级编程与Netty入门10;
import java.net.InetSocketAddress;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
class ClientHanlder extends SimpleChannelHandler{
// 通道被关闭的时候会触发
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelClosed(ctx, e);
System.out.println("channelClosed");
}
// 必须要建立连接,关闭通道的时候才会触发
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelDisconnected(ctx, e);
System.out.println("channelDisconnected");
}
// 接受出现异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
super.exceptionCaught(ctx, e);
System.out.println("exceptionCaught");
}
// 接受客户端数据..
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
super.messageReceived(ctx, e);
System.out.println("messageReceived");
System.out.println("服务器向客户端回复的内容:" + e.getMessage());
}
}
//netty客户端
public class NettyClinet {
public static void main(String[] args) {
// 1.创建服务对象
ClientBootstrap clientBootstrap = new ClientBootstrap();//ClientBootstrap也是是一个帮助类,用来创建客户端的Channel以用来连接到服务端。
// 2.创建两个线程池 第一个 监听端口号 nio监听
ExecutorService boos = Executors.newCachedThreadPool();
ExecutorService wook = Executors.newCachedThreadPool();
// 3.将线程池放入工程
clientBootstrap.setFactory(new NioClientSocketChannelFactory(boos, wook));
// 4.设置管道工程
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
// 设置管道
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = org.jboss.netty.channel.Channels.pipeline();
// 传输数据的时候直接为string类型
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 设置事件监听类
pipeline.addLast("clientHanlder", new ClientHanlder());
return pipeline;
}
});
// 绑定端口号
ChannelFuture connect = clientBootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
Channel channel = connect.getChannel();
System.out.println("client start");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入内容:");
channel.write(scanner.next());
}
}
}
ClientBootstrap介绍
参考
https://blog.csdn.net/weixin_34402408/article/details/91650745