IO
IO Server
public class SocketServer {
public static void main(String[] args) {
//server編號(hào)和client編號(hào)對(duì)應(yīng),優(yōu)缺點(diǎn)注釋在server端
//server1();
//server2();
server3();
}
/**
* server1的缺點(diǎn):
* 1、accept()方法阻塞了線程,要等客戶端連接后才能繼續(xù)處理(一個(gè)客戶端連接,對(duì)應(yīng)一個(gè)服務(wù)端處理線程)
* 2、當(dāng)客戶端與服務(wù)端完成一次交互后,程序就結(jié)束了
* 3、流關(guān)閉代碼比較臃腫、BufferedWriter的內(nèi)容需要在末尾加'\n'
* 4、交互的內(nèi)容寫死,不可以通過控制臺(tái)輸入
* 5、端口號(hào)固定、ip固定、readLine讀取一行等等
*/
public static void server1() {
ServerSocket serverSocket = null;
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
serverSocket = new ServerSocket(8999);
//阻塞至客戶端連接成功
Socket socket = serverSocket.accept();
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//readLine會(huì)阻塞
String s = bufferedReader.readLine();
System.out.println("服務(wù)端接收到客戶端的消息:" + s);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("服務(wù)端發(fā)送消息給客戶端\n");
bufferedWriter.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//各個(gè)try catch 必須分開寫,聚合成一個(gè)會(huì)導(dǎo)致一個(gè)流關(guān)閉異常,無法進(jìn)入其他流的關(guān)閉流程
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//只需要關(guān)閉外層的緩沖區(qū),其close方法內(nèi)部關(guān)閉了了傳入的流
//如果不關(guān)閉,只是等待jvm兜底finally,會(huì)在不確定的時(shí)間內(nèi)對(duì)資源造成占用(如果是文件讀寫會(huì)對(duì)文件描述符fd的占用)
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* server2優(yōu)點(diǎn):
* 1、服務(wù)端和客戶端可以任意次交互,可以連續(xù)發(fā)送消息
* 2、優(yōu)化了流關(guān)閉的寫法
*
* server2的缺點(diǎn):
* 1、accept()方法阻塞了線程,要等客戶端連接后才能繼續(xù)處理(一個(gè)客戶端連接,對(duì)應(yīng)一個(gè)服務(wù)端處理線程)
* 2、cpu空轉(zhuǎn)
* 3、ip固定等等
* 4、一個(gè)服務(wù)端只能和一個(gè)客戶端交互
*/
public static void server2() {
System.out.println("請(qǐng)輸入端口號(hào),并等待客戶端連接...");
try (BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
ServerSocket serverSocket = new ServerSocket(Integer.valueOf(consoleReader.readLine()));
Socket socket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
){
System.out.println("客戶端已連接,開始和客戶端交互...");
//這個(gè)循環(huán)保證可以連續(xù)發(fā)送或者接收多條消息
while (true) {
if (bufferedReader.ready()) {
System.out.println("收到客戶端的消息:" + bufferedReader.readLine());
}
if (consoleReader.ready()) {
printWriter.println(consoleReader.readLine());
printWriter.flush();
}
}
//只能發(fā)一次接收一次
// while (true) {
// System.out.println("收到客戶端的消息:" + bufferedReader.readLine());
//
// printWriter.println(consoleReader.readLine());
// printWriter.flush();
// }
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* server3優(yōu)點(diǎn):
* 一個(gè)服務(wù)端可以和一個(gè)客戶端交互
* 加上了線程池
*
* server3的缺點(diǎn):
* 1、accept()方法阻塞了線程,要等客戶端連接后才能繼續(xù)處理(一個(gè)客戶端連接,對(duì)應(yīng)一個(gè)服務(wù)端處理線程)
* 2、cpu空轉(zhuǎn)
* 3、ip固定等等
* 4、如果開啟多個(gè)客戶端,因System.in被服務(wù)端共享,所以服務(wù)端發(fā)送消息后,客戶端會(huì)隨機(jī)接收其中個(gè)別消息
*/
public static void server3() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(0, Runtime.getRuntime().availableProcessors() * 2,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
System.out.println("請(qǐng)輸入端口號(hào),并等待客戶端連接...");
try (BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
ServerSocket serverSocket = new ServerSocket(Integer.valueOf(consoleReader.readLine()));
){
//這個(gè)循環(huán)保證多個(gè)客戶端連接
while (true) {
Thread.sleep(1000);
Socket socket = serverSocket.accept();
System.out.println("客戶端已連接,開始和客戶端交互...");
pool.submit(new Thread(()->{
try(
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
) {
while (true) {
Thread.sleep(1000);
if(bufferedReader.ready()) {
System.out.println("收到客戶端"+ Thread.currentThread() +"的消息:" + bufferedReader.readLine());
}
if (consoleReader.ready()) {
printWriter.println(consoleReader.readLine());
printWriter.flush();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}));
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
IO Client
public class SocketClient {
public static void main(String[] args) {
//client1();
//client2();
client3();
}
public static void client1() {
Socket socket = null;
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
socket = new Socket("127.0.0.1", 8999);
bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//換行符必不可少,不然服務(wù)端readLine()無法判斷一行已經(jīng)寫完
bufferedWriter.write("客戶端發(fā)送消息給服務(wù)端\n");
bufferedWriter.flush();
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = bufferedReader.readLine();
System.out.println("客戶端接收到服務(wù)端的消息:" + s);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static void client2() {
System.out.println("請(qǐng)輸入端口號(hào):");
try (
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
Socket socket = new Socket("127.0.0.1", Integer.valueOf(consoleReader.readLine()));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
){
System.out.println("連接成功,開始和服務(wù)端交互");
//可以連續(xù)發(fā)送或者接收多條消息
while (true) {
Thread.sleep(1000);
if (bufferedReader.ready()) {
System.out.println("收到服務(wù)端的消息:" + bufferedReader.readLine());
}
if (consoleReader.ready()) {
printWriter.println(consoleReader.readLine());
printWriter.flush();
}
}
//只能發(fā)一次接收一次
// while (true) {
// printWriter.println(consoleReader.readLine());
// printWriter.flush();
//
// System.out.println("收到服務(wù)端的消息:" + bufferedReader.readLine());
// }
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void client3() {
System.out.println("請(qǐng)輸入端口號(hào):");
try (
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
Socket socket = new Socket("127.0.0.1", Integer.valueOf(consoleReader.readLine()));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
){
System.out.println("連接成功,開始和服務(wù)端交互");
//可以連續(xù)發(fā)送或者接收多條消息
while (true) {
if (bufferedReader.ready()) {
System.out.println("收到服務(wù)端的消息:" + bufferedReader.readLine());
}
if (consoleReader.ready()) {
printWriter.println(consoleReader.readLine());
printWriter.flush();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
NIO
NIO Server
public class SocketServer {
public static void main(String[] args) {
//server1();
//server2();
//server3();
//server4();
server5();
}
/**
* 優(yōu)點(diǎn):NIO非阻塞方式,連接不會(huì)阻塞、讀寫不會(huì)阻塞
* 缺點(diǎn):
* 1、當(dāng)客戶端與服務(wù)端完成一次交互后,程序就結(jié)束了
* 2、交互的內(nèi)容寫死,不可以通過控制臺(tái)輸入
* 3、cpu空轉(zhuǎn)
* 4、未使用selector多路復(fù)用器,其編程其實(shí)還是類似與BIO形式(服務(wù)端還是每個(gè)線程對(duì)應(yīng)一個(gè)客戶端)
*/
private static void server1() {
try (
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
){
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
//accept設(shè)置了非阻塞,因此需要放置在循環(huán)中使用,否則程序直接結(jié)束了
while (true) {
Thread.sleep(1000);
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null) {
System.out.println("客戶端已連接,開始和客戶端交互...");
socketChannel.configureBlocking(false);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
socketChannel.read(readBuffer);
readBuffer.clear();
System.out.println("接收到客戶端消息: " + new String(readBuffer.array(), "utf-8").trim());
ByteBuffer writeBuffer = ByteBuffer.wrap("發(fā)送消息給客戶端".getBytes());
socketChannel.write(writeBuffer);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 改良:
* 1、支持客戶端和服務(wù)端一對(duì)一交互,多發(fā)
* 缺點(diǎn):
* 1、不支持一個(gè)客戶端對(duì)多個(gè)服務(wù)端
* 2、如果開啟多個(gè)客戶端,因System.in被服務(wù)端共享,所以服務(wù)端發(fā)送消息后,客戶端會(huì)隨機(jī)接收其中個(gè)別消息
* 3、cpu空轉(zhuǎn)
* 4、未使用selector多路復(fù)用器,其編程其實(shí)還是類似與BIO形式(服務(wù)端還是每個(gè)線程對(duì)應(yīng)一個(gè)客戶端)
*/
private static void server2() {
try (
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
){
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
//accept設(shè)置了非阻塞,因此需要放置在循環(huán)中使用,否則程序直接結(jié)束了
while (true) {
Thread.sleep(1000);
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null) {
System.out.println("客戶端已連接,開始和客戶端交互...");
socketChannel.configureBlocking(false);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (true) {
if(socketChannel.read(readBuffer) > 0) {
System.out.println("接收到客戶端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
if (consoleReader.ready()) {
ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
socketChannel.write(writeBuffer);
writeBuffer.clear();
}
Thread.sleep(1000);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 改良:
* 1、支持一個(gè)客戶端對(duì)多個(gè)服務(wù)端
* 缺點(diǎn):
* 1、cpu空轉(zhuǎn)
* 2、未使用selector多路復(fù)用器,其編程其實(shí)還是類似與BIO形式(服務(wù)端還是每個(gè)線程對(duì)應(yīng)一個(gè)客戶端)
*/
private static void server3() {
try (
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
) {
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
List<SocketChannel> socketChannels = new ArrayList<>();
//accept設(shè)置了非阻塞,因此需要放置在循環(huán)中使用,否則程序直接結(jié)束了
while (true) {
Thread.sleep(1000);
boolean ready = consoleReader.ready();
byte[] bytes = null;
if (ready) {
bytes = consoleReader.readLine().getBytes();
}
//調(diào)用一次accept再次使用會(huì)變成成null,除非有新的客戶端連接
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
socketChannels.add(socketChannel);
socketChannel.configureBlocking(false);
System.out.println("客戶端已連接,開始和客戶端交互...");
}
for (SocketChannel channel : socketChannels) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
if (channel.read(readBuffer) > 0) {
System.out.println("接收到客戶端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
if (ready) {
ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
channel.write(writeBuffer);
writeBuffer.clear();
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 改良:
* 1、支持一個(gè)客戶端對(duì)多個(gè)服務(wù)端 (服務(wù)端發(fā)送消息,客戶端都能收到,所有客戶端發(fā)送消息服務(wù)端都能收到)
* 2、相對(duì)與server3多線程方式,服務(wù)端只需要啟動(dòng)一個(gè)主線程即可與所有客戶端交互,
* 缺點(diǎn):
* 1、cpu空轉(zhuǎn)
* 2、未使用selector多路復(fù)用器,其編程其實(shí)還是類似與BIO形式(服務(wù)端還是每個(gè)線程對(duì)應(yīng)一個(gè)客戶端)
*/
static final List<SocketChannel> socketChannels4 = new CopyOnWriteArrayList<>();
private static void server4() {
try (
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
) {
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
//accept設(shè)置了非阻塞,因此需要放置在循環(huán)中使用,否則程序直接結(jié)束了
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
System.out.println("客戶端已連接,開始和客戶端交互...");
socketChannel.configureBlocking(false);
socketChannels4.add(socketChannel);
}
Iterator<SocketChannel> iterator = socketChannels4.iterator();
boolean ready = consoleReader.ready();
byte[] writeBytes = null;
if (ready) {
writeBytes = consoleReader.readLine().getBytes();
}
while (iterator.hasNext()) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
SocketChannel channel = iterator.next();
if (channel.read(readBuffer) > 0) {
System.out.println("接收到客戶端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
if (ready) {
ByteBuffer writeBuffer = ByteBuffer.wrap(writeBytes);
channel.write(writeBuffer);
writeBuffer.clear();
}
}
Thread.sleep(1000);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* 改良:
* 1、支持一個(gè)客戶端對(duì)多個(gè)服務(wù)端 (服務(wù)端發(fā)送消息,客戶端都能收到,所有客戶端發(fā)送消息服務(wù)端都能收到)
* 2、相對(duì)與server3多線程方式,服務(wù)端只需要啟動(dòng)一個(gè)主線程即可與所有客戶端交互,
* 缺點(diǎn):
* 1、cpu空轉(zhuǎn)
* 2、未使用selector多路復(fù)用器,其編程其實(shí)還是類似與BIO形式(服務(wù)端還是每個(gè)線程對(duì)應(yīng)一個(gè)客戶端)
*/
private static void server5() {
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
Selector selector = Selector.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
) {
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
//將serverSocketChannel注冊(cè)到selector, 并監(jiān)聽accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
List<SocketChannel> socketChannels = new ArrayList<>();
while (true) {
//非阻塞
selector.select(1000);
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if(selectionKey.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("客戶端已連接,開始和客戶端交互...");
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
socketChannels.add(socketChannel);
} else if(selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
if (socketChannel.read(readBuffer) > 0) {
System.out.println("接收到客戶端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
}
iterator.remove();
}
if(consoleReader.ready()) {
byte[] bytes = consoleReader.readLine().getBytes();
for (SocketChannel socketChannel : socketChannels) {
ByteBuffer writeBuffer = ByteBuffer.wrap(bytes);
socketChannel.write(writeBuffer);
writeBuffer.clear();
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
NIO Client
public class SocketClient {
public static void main(String[] args) {
//client1();
//client2_3_4();
client5();
}
private static void client1() {
try (
SocketChannel socketChannel = SocketChannel.open();
){
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
while (!socketChannel.finishConnect()) {
Thread.sleep(1000);
System.out.println("正在連接客戶端...");
}
ByteBuffer writeBuffer = ByteBuffer.wrap("向服務(wù)端發(fā)送消息".getBytes());
socketChannel.write(writeBuffer);
writeBuffer.clear();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (true) {
if(socketChannel.read(readBuffer) > 0) {
System.out.println("接收到服務(wù)端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
break;
}
Thread.sleep(1000);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private static void client2_3_4() {
try (
SocketChannel socketChannel = SocketChannel.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
){
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
while (!socketChannel.finishConnect()) {
Thread.sleep(1000);
System.out.println("正在連接客戶端...");
}
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (true) {
if(socketChannel.read(readBuffer) > 0) {
System.out.println("接收到服務(wù)端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
if (consoleReader.ready()) {
ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
socketChannel.write(writeBuffer);
writeBuffer.clear();
}
Thread.sleep(1000);
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private static void client5() {
try (
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
Selector selector = Selector.open();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
){
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
while (true) {
selector.select(1000);
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isReadable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
if(channel.read(readBuffer) > 0) {
System.out.println("接收到服務(wù)端消息: " + new String(readBuffer.array(), "utf-8").trim());
readBuffer.clear();
}
}
iterator.remove();
}
if (consoleReader.ready()){
ByteBuffer writeBuffer = ByteBuffer.wrap(consoleReader.readLine().getBytes());
socketChannel.write(writeBuffer);
writeBuffer.clear();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-774150.html
文章來源:http://www.zghlxwxcb.cn/news/detail-774150.html
到了這里,關(guān)于IO/NIO交互模擬及漸進(jìn)式實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!