前言:
Java I/O模型發(fā)展以及Netty網(wǎng)絡(luò)模型的設(shè)計(jì)思想
BIO
Java BIO是Java平臺(tái)上的BIO(Blocking I/O)模型,是Java中用于實(shí)現(xiàn)同步阻塞網(wǎng)絡(luò)編程的一種方式。 在Java中,使用BIO模型需要通過(guò)Socket和ServerSocket類來(lái)完成網(wǎng)絡(luò)連接和數(shù)據(jù)傳輸,但是由于BIO是同步阻塞的,所以會(huì)導(dǎo)致線程阻塞和資源浪費(fèi)的問(wèn)題。
因此,在高并發(fā)的網(wǎng)絡(luò)編程場(chǎng)景中,通常會(huì)選擇使用NIO(Non-blocking I/O)模型或者Netty等框架來(lái)實(shí)現(xiàn)。
服務(wù)端類代碼
package com.yu.io.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class BIOServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8081);
while (true) {
System.out.println("wait connect...");
Socket clientSocket = serverSocket.accept();
System.out.println(clientSocket.getPort() + "connected");
System.out.println("start read...");
byte[] readArr = new byte[1024];
int read = clientSocket.getInputStream().read(readArr);
if (read != -1) {
System.out.println("read info : " + new String(readArr,0,read));
}
System.out.println("end read...");
byte[] resBytes = "server response".getBytes();
clientSocket.getOutputStream().write(resBytes);
System.out.println("response info : " + new String(readArr,0,read));
clientSocket.getOutputStream().flush();
}
}
}
客戶端類代碼
package com.yu.io.bio;
import java.io.IOException;
import java.net.Socket;
public class BIOClient {
public static void main(String[] args) throws IOException {
Socket clientSocket = new Socket("127.0.0.1",8081);
byte[] resBytes = "client response".getBytes();
clientSocket.getOutputStream().write(resBytes);
System.out.println("response info : " + new String(resBytes));
clientSocket.getOutputStream().flush();
byte[] readArr = new byte[1024];
int read = clientSocket.getInputStream().read(readArr);
if (read != -1) {
System.out.println("read info : " + new String(readArr,0,read));
}
}
}
NIO
Java NIO 能夠支持非阻塞網(wǎng)絡(luò)編程,可以理解為new io 或者no blok io 我更喜歡稱之為new io,因?yàn)樗粌H僅實(shí)現(xiàn)了非阻塞的網(wǎng)絡(luò)編程方式,同時(shí)也封裝了常用的網(wǎng)絡(luò)編程api,更重要的是引入了多路復(fù)用器Selector的概念
下面的代碼只是展示NIO非阻塞的實(shí)現(xiàn),并沒(méi)有展示NIO的真正用法
NIO演示(無(wú)Selector)
服務(wù)端類代碼
package com.yu.io.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
public class NIOServer {
public static List<SocketChannel> socketChannelList = new ArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8081));
serverSocketChannel.configureBlocking(false);
System.out.println("server start...");
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
System.out.println(socketChannel.socket().getPort() + " connected");
socketChannel.configureBlocking(false);
socketChannelList.add(socketChannel);
}
List<SocketChannel> rmChannelList = new ArrayList<>();
for (SocketChannel channel : socketChannelList) {
try {
doChannel(rmChannelList, channel);
} catch (IOException ioException) {
//有客戶端斷開連接
System.out.println(channel.socket().getPort() + " disconnected");
channel.close();
rmChannelList.add(channel);
}
}
socketChannelList.removeAll(rmChannelList);
}
}
private static void doChannel(List<SocketChannel> rmChannelList, SocketChannel channel) throws IOException {
ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);
int read = channel.read(readByteBuffer);
String readStr = new String(readByteBuffer.array());
if (read > 0) {
System.out.println(channel.socket().getPort() + " : " + readStr);
if (readStr.contains("hello")) {
ByteBuffer sendByteBuffer = ByteBuffer.wrap("hello! I am robot.".getBytes());
channel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
}
if (readStr.contains("old")) {
ByteBuffer sendByteBuffer = ByteBuffer.wrap("I am 1 years old.".getBytes());
channel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
}
if (readStr.contains("bey")) {
ByteBuffer sendByteBuffer = ByteBuffer.wrap("see you.".getBytes());
channel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
}
}
if (read == -1) {
rmChannelList.add(channel);
}
}
}
客戶端類代碼
package com.yu.io.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NIOClient {
public static void main(String[] args) throws IOException {
SocketChannel clientSocketChannel = SocketChannel.open();
clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",8081));
clientSocketChannel.configureBlocking(false);
String sendStr = "hello! I am client " + clientSocketChannel.socket().getPort() + ".";
ByteBuffer sendByteBuffer = ByteBuffer.wrap(sendStr.getBytes());
clientSocketChannel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
int msgSize = 0;
while (msgSize < 10) {
ByteBuffer readByteBuffer = ByteBuffer.allocate(1024);
int read = clientSocketChannel.read(readByteBuffer);
String readStr = new String(readByteBuffer.array());
if (read > 0) {
System.out.println("robot : " + readStr);
msgSize ++;
ByteBuffer resByteBuffer = null;
if (readStr.contains("hello")) {
resByteBuffer = ByteBuffer.wrap("how old are you?.".getBytes());
clientSocketChannel.write(resByteBuffer);
System.out.println("me : " + new String(resByteBuffer.array()));
resByteBuffer.clear();
}
if (readStr.contains("old")) {
resByteBuffer = ByteBuffer.wrap("en, place say hello!".getBytes());
clientSocketChannel.write(resByteBuffer);
System.out.println("me : " + new String(resByteBuffer.array()));
resByteBuffer.clear();
}
}
}
ByteBuffer resByteBuffer = ByteBuffer.wrap("bey bey!".getBytes());
clientSocketChannel.write(resByteBuffer);
System.out.println("me : " + new String(resByteBuffer.array()));
resByteBuffer.clear();
clientSocketChannel.close();
}
}
NIO演示(Selector)
無(wú)Selector的NIO演示中,顯然會(huì)出現(xiàn)空轉(zhuǎn)的情況,以及無(wú)效連接的處理問(wèn)題,這些問(wèn)題都會(huì)影響性能。
NIO提供Selector多路復(fù)用器,優(yōu)化上述問(wèn)題
以下demo實(shí)現(xiàn)服務(wù)器與客戶端通信,相互發(fā)消息。并由服務(wù)器轉(zhuǎn)發(fā)給其他客戶端(廣播功能)
服務(wù)端類代碼
server main類
import java.io.IOException;
public class NIOServerMain {
public static void main(String[] args) throws IOException {
NIOSelectorServer server = new NIOSelectorServer();
server.start();
}
}
server run類
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class NIOSelectorServer {
public void start() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8081));
serverSocketChannel.configureBlocking(false);
//注冊(cè)到selector多路復(fù)用器中
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("server start...");
run(selector);
}
/**
* 遍歷多路復(fù)用器的事件,處理事件
*/
private void run(Selector selector) throws IOException {
while (true) {
//阻塞等待事件
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectedKey = iterator.next();
try {
if (selectedKey.isAcceptable()) {
doAccept(selector, selectedKey);
}
if (selectedKey.isReadable()) {
doReadChannel(selectedKey, selector);
}
} catch (IOException ioException) {
//有客戶端斷開連接
disConnect(selectedKey, "exception");
}
iterator.remove();
}
}
}
/**
* 處理連接事件
*/
private void doAccept(Selector selector, SelectionKey selectedKey) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel)selectedKey.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println(socketChannel.socket().getPort() + " connected");
}
/**
* 處理接收信息事件
*/
private void doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {
SocketChannel channel = (SocketChannel)selectedKey.channel();
ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);
int read = channel.read(readByteBuffer);
String readStr = "";
readByteBuffer.flip();
readStr += StandardCharsets.UTF_8.decode(readByteBuffer);
if (read > 0) {
System.out.println(channel.socket().getPort() + " : " + readStr.length() + " - "+ readStr);
//轉(zhuǎn)發(fā)消息(其他客戶端)
broadcast(selectedKey, selector, readStr);
if (readStr.contains("hello")) {
sendMsg(channel, "hello! I am robot.");
}
if (readStr.contains("old")) {
sendMsg(channel, "I am 1 years old.");
}
if (readStr.contains("bye")) {
sendMsg(channel, "see you.");
}
}
if (read == -1) {
//有客戶端斷開連接
disConnect(selectedKey, "read = -1");
}
}
/**
* 連接異常的處理
*/
private void disConnect(SelectionKey selectedKey, String type) throws IOException {
SocketChannel channel = (SocketChannel)selectedKey.channel();
System.out.println(channel.socket().getPort() + " disconnected. " + type);
selectedKey.cancel();
channel.close();
}
/**
* 發(fā)送消息
*/
private void sendMsg(SocketChannel channel, String s) throws IOException {
ByteBuffer sendByteBuffer = ByteBuffer.wrap(s.getBytes());
channel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
}
/**
* 廣播
* 轉(zhuǎn)發(fā)消息(給其他客戶端)
*/
private void broadcast(SelectionKey fromSelectedKey, Selector selector, String readStr) throws IOException {
Iterator<SelectionKey> selectionKeyIterator = selector.keys().iterator();
while (selectionKeyIterator.hasNext()) {
SelectionKey otherKey = selectionKeyIterator.next();
if (otherKey == fromSelectedKey) {
continue;
}
if (!(otherKey.channel() instanceof SocketChannel)) {
continue;
}
SocketChannel otherChannel = (SocketChannel)otherKey.channel();
sendMsg(otherChannel, "(轉(zhuǎn)發(fā)自 "+ ((SocketChannel)fromSelectedKey.channel()).socket().getPort() + ")" + readStr);
}
}
}
客戶端代碼
一共構(gòu)造了兩個(gè)客戶端(消息客戶端和looker客戶端), looker客戶端優(yōu)先啟動(dòng),隨后啟動(dòng)消息客戶端,消息客戶端與服務(wù)器的通信會(huì)被轉(zhuǎn)發(fā)給looker客戶端
look client main類
import java.io.IOException;
public class NIOClientLookMain {
public static void main(String[] args) throws IOException, InterruptedException {
NIOSelectorClient client = new NIOSelectorLookClient();
client.start(8081, 100);
}
}
msg client main類
import java.io.IOException;
public class NIOClientMain {
public static void main(String[] args) throws IOException, InterruptedException {
NIOSelectorClient lookClient = new NIOSelectorClient();
lookClient.start(8081, 10);
}
}
client run類文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-697245.html
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.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
public class NIOSelectorClient {
protected int port;
protected int size;
public void start(int port, int size) throws IOException, InterruptedException {
this.port = port;
this.size = size;
SocketChannel clientSocketChannel = SocketChannel.open();
clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",port));
clientSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
clientSocketChannel.register(selector, SelectionKey.OP_READ);
//發(fā)送開始數(shù)據(jù)
sendMsg(clientSocketChannel, "hello! I am client " + clientSocketChannel.socket().getLocalPort() + ".");
run(selector);
sendMsg(clientSocketChannel, "bye bye!");
clientSocketChannel.close();
}
/**
* 遍歷多路復(fù)用器的事件,處理事件
*/
protected void run(Selector selector) throws IOException, InterruptedException {
int msgSize = 0;
while (msgSize < size) {
int length=selector.select();
if(length ==0){
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
boolean readChannel = false;
try {
readChannel = doReadChannel(selectionKey, selector);
} catch (IOException e) {
selectionKey.cancel();
System.out.println("robot disconnect, restart connect...");
while (true){
try {
reConnect();
return;
} catch (IOException ioException) {
System.out.println("restart connecting(5s) ");
//ioException.printStackTrace();
Thread.sleep(5000);
}
}
}
if (readChannel) {
msgSize ++;
}
}
iterator.remove();
}
}
}
protected boolean doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {
SocketChannel channel = (SocketChannel)selectedKey.channel();
ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);
int read = channel.read(readByteBuffer);
String readStr = "";
readByteBuffer.flip();
readStr += StandardCharsets.UTF_8.decode(readByteBuffer);
if (read > 0) {
System.out.println("robot : " + readStr);
if (readStr.contains("hello")) {
sendMsg(channel, "how old are you?.");
}
if (readStr.contains("old")) {
sendMsg(channel, "en, place say hello!");
}
return true;
}
return false;
}
protected void sendMsg(SocketChannel channel, String sendStr) throws IOException {
ByteBuffer sendByteBuffer = ByteBuffer.wrap(sendStr.getBytes());
channel.write(sendByteBuffer);
System.out.println("me : " + new String(sendByteBuffer.array()));
}
protected void reConnect() throws IOException, InterruptedException {
NIOSelectorClient client = new NIOSelectorClient();
client.start(port, size);
}
}
look client run類文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-697245.html
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.SocketChannel;
import java.nio.charset.StandardCharsets;
public class NIOSelectorLookClient extends NIOSelectorClient{
@Override
public void start(int port, int size) throws IOException, InterruptedException {
this.port = port;
this.size = size;
SocketChannel clientSocketChannel = SocketChannel.open();
clientSocketChannel.connect(new InetSocketAddress("127.0.0.1",port));
clientSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
clientSocketChannel.register(selector, SelectionKey.OP_READ);
//發(fā)送開始數(shù)據(jù)
sendMsg(clientSocketChannel, "I am looker. " + clientSocketChannel.socket().getLocalPort() + ".");
run(selector);
}
@Override
protected boolean doReadChannel(SelectionKey selectedKey, Selector selector) throws IOException {
SocketChannel channel = (SocketChannel)selectedKey.channel();
ByteBuffer readByteBuffer = ByteBuffer.allocate(2048);
int read = channel.read(readByteBuffer);
String readStr = "";
readByteBuffer.flip();
readStr += StandardCharsets.UTF_8.decode(readByteBuffer);
if (read > 0) {
System.out.println("robot : " + readStr.length() + " - "+ readStr);
if (readStr.contains("bye")) {
sendMsg(channel, "bye.");
}
return true;
}
return false;
}
@Override
protected void reConnect() throws IOException, InterruptedException {
NIOSelectorClient client = new NIOSelectorLookClient();
client.start(port, size);
}
}
到了這里,關(guān)于Java分別用BIO、NIO實(shí)現(xiàn)簡(jiǎn)單的客戶端服務(wù)器通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!