通信におけるクライアント・サーバモード.BIO
BIOすなわち、IOブロッキングIO同期を遮断、BIOの通信モードにおいて、
「スレッドは、受け入れる読み取りまたはIOを結ぶ線に対応し、ブロックの書き込み方法いずれかであることができます」
サーバモード通信BIOで、すなわちクライアントプログラム:
- BIOClient
/**
* @Auther: ARong
* @Description: BIO 的 客户端
*/
public class BIOClient {
public static void main(String[] args) throws IOException, InterruptedException {
Socket socket = new Socket("127.0.0.1", 6666);
// 每2s向服务端发送helloword
for (int i = 0; i < 10; i++) {
Thread.sleep(2*1000);
String message = String.format("HelloWorld From %s", new SimpleDateFormat("hh:mm:ss").format(new Date()));
socket.getOutputStream().write(message.getBytes());
}
}
}
- BIOServer
/**
* @Auther: ARong
* @Description: BIO 的 服务端
*/
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(6666);
while (true) {
// 阻塞直到有客户端连接
Socket socket = serverSocket.accept();
// 每次有连接即创建一个线程接受处理
new Thread(()->{
// 处理请求
int len = -1;
byte[] data = new byte[1024];//每次读取1kb
try {
InputStream inputStream = socket.getInputStream();
while ((len = inputStream.read(data)) != -1) {
System.out.println(new String(data, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
成功した各接続のための伝統的なモデルはBIOがスレッドを維持する必要が確立され、各スレッドがwhileループ、それに対応するコネクション1ワット1ワットスレッド、その後、しばらく無限ループ1W以下の問題をもたらすことが含まれています。
- スレッドのリソースに制約:ブロックされた状態で多数のスレッドは、オペレーティングシステムが消費する余裕がない資源の非常に深刻な廃棄物である、オペレーティングシステムは非常に貴重な資源であると同時にスレッド
- 低スレッドの切り替え効率:シングルCPUコア固定数、オペレーティング・システム・スレッド頻繁なスレッド切り替えの爆発、アプリケーションのパフォーマンスが大幅に減少した後
- データはバイトストリームの単位で読まれることは効率的ではありません:バイトは1バイトのデータであるたびに基盤となるオペレーティングシステムから読み取ります
第2の通信モード.NIO
新しいIOまたはNIOすなわちNoBloking-IO、同期してIOを、ノンブロッキングベースのバッファ、チャネルセレクタおよび「IO多重通信モードは、スレッドが私達accpet、読み取りまたは書き込み方法でブロックされていないが、同時に接続された複数のIO線を扱うことができます。」
BIOの利点に比べNIOは、以下のとおりです。
- IO接続でブロックすることなくIO、IOの複数を処理する様式を多重化を介してスレッドの少量のみ、スレッドコンテキストスイッチは、本問題は、通信効率を改善しないであろう
- バイトストリームデータがもはや単位として読み取るが、ブロック単位で読み取りおよび書き込みバッファ
以下は、NIOデモのJava実装であります
- NIOServer(フラッシュからコピー)
public class NIOServer {
/**
* serverSelector负责轮询是否有新的连接,clientSelector负责轮询连接是否有数据可读.
* 服务端监测到新的连接不再创建一个新的线程,而是直接将新连接绑定到clientSelector上,这样不用IO模型中1w个while循环在死等
* clientSelector被一个while死循环包裹,如果在某一时刻有多条连接有数据可读通过 clientSelector.select(1)方法轮询出来进而批量处理
* 数据的读写以内存块为单位
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Selector serverSelector = Selector.open();
Selector clientSelector = Selector.open();
new Thread(() -> {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8000));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
while (true) {
// 轮询监测是否有新的连接
if (serverSelector.select(1) > 0) {
Set<SelectionKey> selectionKeys = serverSelector.selectedKeys();
Iterator<SelectionKey> keyIterator;
keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
if (selectionKey.isAcceptable()) {
try {
//(1)每来一个新连接不需要创建一个线程而是直接注册到clientSelector
SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
socketChannel.configureBlocking(false);
socketChannel.register(clientSelector, SelectionKey.OP_READ);
} finally {
keyIterator.remove();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
while (true) {
// (2)批量轮询是否有哪些连接有数据可读
if (clientSelector.select(1) > 0) {
Set<SelectionKey> selectionKeys = serverSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectionKey = keyIterator.next();
if (selectionKey.isReadable()) {
try {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//(3)读取数据以块为单位批量读取
socketChannel.read(byteBuffer);
byteBuffer.flip();
System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
.toString());
} finally {
keyIterator.remove();
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
3 .Nettyを取得
1.ネッティーとは何ですか
非ブロッキング非同期、イベント駆動型、高性能、高信頼性と高いカスタマイズ:網状は、Java NIOライブラリに基づいて、非同期通信フレームワークであり、そのアーキテクチャは、によって特徴づけられます。ネッティーの使用は、JavaのNIOライブラリを使用することは困難で良い代替アウト煩雑になる可能性があり、かつネッティーは、利用可能な多くの機能を提供します。
2.Nettyの実用化
- 高性能のHTTPサーバ
- 高性能RPCフレーム(例えばダボ)
- Redission(RedisのためのJava)
- IMインスタントメッセージングアプリケーション
- それは達成するための基盤となるネットワーク通信フレームの大規模な番号が必要です
3.Nettyはデモはじめに
- 輸入依存度の達人
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
- NIOServer
public class NIOServer {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
NioEventLoopGroup bossGroup = new NioEventLoopGroup(); // 监听组
NioEventLoopGroup workerGroup = new NioEventLoopGroup(); // 工作组
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) //NIO模式
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
// 初始化channel
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(String.format("服务端接收到消息:%s", msg));
}
});
}
}).bind(8000);
}
}
- NIOCLient
public class NIOClient {
public static void main(String[] args) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder());
}
});
Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
while (true) {
String message = String.format("HelloWorld From %s", new SimpleDateFormat("hh:mm:ss").format(new Date()));
channel.writeAndFlush(message);
Thread.sleep(2000);
}
}
}
プログラムの実行: