專門(mén)做瓷磚的網(wǎng)站百度推廣客服工作怎么樣
文章目錄
- Java網(wǎng)絡(luò)編程原理與實(shí)踐--從Socket到BIO再到NIO
- Socket基本架構(gòu)
- Socket 基本使用
- 簡(jiǎn)單一次發(fā)送接收
- 客戶端
- 服務(wù)端
- 字節(jié)流方式簡(jiǎn)單發(fā)送接收
- 客戶端
- 服務(wù)端
- 雙向通信
- 客戶端
- 服務(wù)端
- 多次接收消息
- 客戶端
- 服務(wù)端
- Socket寫(xiě)法的問(wèn)題
- BIO
- 簡(jiǎn)單流程
- BIO寫(xiě)法
- 客戶端
- 服務(wù)端
- BIO的問(wèn)題
- NIO
- 簡(jiǎn)述
- Buffer
- Channel(通道)
- Selector(選擇器)
- 基本介紹
- 使用實(shí)例
Java網(wǎng)絡(luò)編程原理與實(shí)踐–從Socket到BIO再到NIO
Socket基本架構(gòu)
圖來(lái)源:https://zhuanlan.zhihu.com/p/462497498
既然是網(wǎng)絡(luò)的東西肯定得放個(gè)網(wǎng)絡(luò)架構(gòu)圖,這張圖不多說(shuō),感興趣可以去鏈接詳細(xì)看一下
Socket 基本使用
轉(zhuǎn)自:https://blog.csdn.net/a78270528/article/details/80318571
簡(jiǎn)單一次發(fā)送接收
客戶端
package Scoket.client;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;/**** 字符流方式*/
public class Client {public static void main(String[] args) {try {// 服務(wù)器的主機(jī)和端口String serverHost = "127.0.0.1";int serverPort = 6443;// 創(chuàng)建Socket對(duì)象,連接到服務(wù)器Socket socket = new Socket(serverHost, serverPort);// 獲取輸入流和輸出流BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter output = new PrintWriter(socket.getOutputStream(), true);// 發(fā)送數(shù)據(jù)給服務(wù)器String messageToSend = "Hello, Server!";output.println(messageToSend);// 接收服務(wù)器的響應(yīng)數(shù)據(jù)String dataReceived = input.readLine();System.out.println("Received from server: " + dataReceived);// 關(guān)閉連接socket.close();} catch (Exception e) {e.printStackTrace();}}
}
服務(wù)端
package Scoket.client;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {try {// 本地主機(jī)和端口String serverHost = "127.0.0.1";int serverPort = 6443;// 創(chuàng)建ServerSocket對(duì)象,綁定地址和端口ServerSocket serverSocket = new ServerSocket(serverPort);System.out.println("Server listening on " + serverHost + ":" + serverPort);// 接受客戶端連接Socket clientSocket = serverSocket.accept();System.out.println("Accepted connection from " + clientSocket.getInetAddress());// 獲取輸入流和輸出流BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));PrintWriter output = new PrintWriter(clientSocket.getOutputStream(), true);// 接收客戶端發(fā)送的數(shù)據(jù)String dataReceived = input.readLine();System.out.println("Received from client: " + dataReceived);// 發(fā)送響應(yīng)給客戶端String messageToSend = "Hello, Client!";output.println(messageToSend);// 關(guān)閉連接clientSocket.close();serverSocket.close();} catch (Exception e) {e.printStackTrace();}}
}
如果進(jìn)行debug會(huì)發(fā)現(xiàn),服務(wù)端代碼總共卡主兩次:
1、 Socket clientSocket = serverSocket.accept(); 這里會(huì)監(jiān)聽(tīng)端口,等待客戶端請(qǐng)求建立連接,實(shí)際上是進(jìn)行三次握手
2、 String dataReceived = input.readLine(); 這里是等待客戶端發(fā)送數(shù)據(jù),接收到數(shù)據(jù)會(huì)進(jìn)行下一步
這兩步驟需要注意,因?yàn)檫@是后面BIO和NIO的優(yōu)化點(diǎn)
字節(jié)流方式簡(jiǎn)單發(fā)送接收
使用字節(jié)流處理,這可能使得處理字符串?dāng)?shù)據(jù)稍顯繁瑣。如果你的通信數(shù)據(jù)是文本,可能使用字符流更為方便。
但是數(shù)據(jù)更可控一些,下面簡(jiǎn)單羅列
客戶端
package Scoket.client;import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;/*** 字節(jié)流方式*/
public class Client1 {public static void main(String[] args) {try {String host = "127.0.0.1";int port = 6443;Socket socket = new Socket(host, port);OutputStream outputStream = socket.getOutputStream();String message = "message, 你好";socket.getOutputStream().write(message.getBytes(StandardCharsets.UTF_8));outputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
}
服務(wù)端
package Scoket.client;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server1 {public static void main(String[] args) {try {int port = 6443;ServerSocket serverSocket = new ServerSocket(port);System.out.println("等待連接");Socket accept = serverSocket.accept();System.out.println("完成連接,等待傳輸數(shù)據(jù)");InputStream inputStream = accept.getInputStream();byte[] bytes = new byte[1024];int len;StringBuilder sb = new StringBuilder();while ((len = inputStream.read(bytes)) != -1){sb.append(new String(bytes, 0, len, "UTF-8"));}System.out.println("get message:" + sb);inputStream.close();accept.close();serverSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
雙向通信
客戶端
package Scoket.client;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;public class Client2 {public static void main(String[] args) {try {String host = "127.0.0.1";int port = 8443;Socket socket = new Socket(host, port);OutputStream outputStream = socket.getOutputStream();outputStream.write("我是客戶,接受一下我的消息".getBytes(StandardCharsets.UTF_8));socket.shutdownOutput();InputStream inputStream = socket.getInputStream();byte[] bytes = new byte[1024];int len;StringBuilder stringBuilder = new StringBuilder();while ((len = inputStream.read(bytes)) != -1){stringBuilder.append(new String(bytes, 0, len, "UTF-8"));}System.out.println("get message:" + stringBuilder);inputStream.close();outputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
}
服務(wù)端
package Scoket.client;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;public class Server2 {public static void main(String[] args) {try {int port = 8443;ServerSocket serverSocket = new ServerSocket(port);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();byte[] bytes = new byte[1024];int len ;StringBuilder sb = new StringBuilder();while ((len = inputStream.read(bytes)) != -1){sb.append(new String(bytes, 0, len, "UTF-8"));}System.out.println("server2 get Message:" + sb);OutputStream outputStream = socket.getOutputStream();outputStream.write("我是服務(wù)器".getBytes(StandardCharsets.UTF_8));inputStream.close();outputStream.close();socket.close();serverSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
多次接收消息
客戶端
package Scoket.client;import java.io.OutputStream;
import java.net.Socket;public class Client3 {public static void main(String args[]) throws Exception {// 要連接的服務(wù)端IP地址和端口String host = "127.0.0.1";int port = 8444;// 與服務(wù)端建立連接Socket socket = new Socket(host, port);// 建立連接后獲得輸出流OutputStream outputStream = socket.getOutputStream();String message = "你好 yiwangzhibujian";//首先需要計(jì)算得知消息的長(zhǎng)度byte[] sendBytes = message.getBytes("UTF-8");//然后將消息的長(zhǎng)度優(yōu)先發(fā)送出去outputStream.write(sendBytes.length >>8);outputStream.write(sendBytes.length);//然后將消息再次發(fā)送出去outputStream.write(sendBytes);outputStream.flush();//==========此處重復(fù)發(fā)送一次,實(shí)際項(xiàng)目中為多個(gè)命名,此處只為展示用法message = "第二條消息";sendBytes = message.getBytes("UTF-8");outputStream.write(sendBytes.length >>8);outputStream.write(sendBytes.length);outputStream.write(sendBytes);outputStream.flush();//==========此處重復(fù)發(fā)送一次,實(shí)際項(xiàng)目中為多個(gè)命名,此處只為展示用法message = "the third message!";sendBytes = message.getBytes("UTF-8");outputStream.write(sendBytes.length >>8);outputStream.write(sendBytes.length);outputStream.write(sendBytes); outputStream.close();socket.close();}
}
服務(wù)端
package Scoket.client;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server3 {public static void main(String[] args) {try {int port = 8444;ServerSocket serverSocket = new ServerSocket(port);Socket accept = serverSocket.accept();InputStream inputStream = accept.getInputStream();byte[] bytes;while (true){int first = inputStream.read();if(first == -1){break;}int second = inputStream.read();int len = (first << 8) +second;bytes = new byte[len];inputStream.read(bytes);System.out.println("Server3 get message:" + new String(bytes, "UTF-8"));}} catch (IOException e) {e.printStackTrace();}}
}
Socket寫(xiě)法的問(wèn)題
上面的代碼有些很大的問(wèn)題
1、阻塞式 I/O: 這是最大的缺點(diǎn)之一。在 accept()、readLine() 等方法調(diào)用時(shí),程序會(huì)被阻塞,等待客戶端連接或數(shù)據(jù)到來(lái)。這可能導(dǎo)致服務(wù)器在處理多個(gè)客戶端時(shí)性能下降。
2、單線程處理: 服務(wù)器采用單線程處理客戶端連接。這意味著一次只能處理一個(gè)客戶端連接,如果有大量的客戶端同時(shí)連接,性能會(huì)受到影響。
3、不適用于高并發(fā): 由于采用單線程處理方式,不適合高并發(fā)環(huán)境。在高并發(fā)情況下,建議考慮使用多線程或異步 I/O 模型。
4、異常處理不足: 缺少一些異常處理,例如,在 accept()、readLine() 中可能會(huì)拋出異常,而在示例中并未捕獲和處理這些異常。
針對(duì)1、2可以采用BIO方式
針對(duì)1、2、3可以采用NIO
接下來(lái)將會(huì)優(yōu)化代碼分別介紹BIO和NIO
BIO
簡(jiǎn)單流程
服務(wù)器啟動(dòng)一個(gè)ServerSocket。
客戶端啟動(dòng)一個(gè)Socket對(duì)服務(wù)器進(jìn)行通信,默認(rèn)情況下,服務(wù)器端需要對(duì)每一個(gè)客戶端建立一個(gè)線程與之通信。
客戶端發(fā)出請(qǐng)求后,先咨詢服務(wù)器是否有線程相應(yīng),如果沒(méi)有則會(huì)等待,或者被拒絕。
如果有響應(yīng),客戶端線程會(huì)等待請(qǐng)求結(jié)束后,再繼續(xù)執(zhí)行。
BIO寫(xiě)法
客戶端
package Scoket.client;import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;public class BIOClient{public static void main(String[] args) {try {Socket socket = new Socket("127.0.0.1", 6666);OutputStream outputStream = socket.getOutputStream();outputStream.write("hi, i am client".getBytes(StandardCharsets.UTF_8));outputStream.flush();socket.close();} catch (IOException e) {e.printStackTrace();}}
}
服務(wù)端
轉(zhuǎn)自:https://juejin.cn/post/6924670437867651080
package Scoket.client;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BIOServer {public static void main(String[] args) throws IOException {// 創(chuàng)建線程池ExecutorService executorService = Executors.newCachedThreadPool();// 創(chuàng)建ServerSocket并且監(jiān)聽(tīng)6666端口ServerSocket serverSocket = new ServerSocket(6666);while (true) {// 監(jiān)聽(tīng)---一直等待客戶端連接Socket socket = serverSocket.accept();// 連接來(lái)了之后,啟用一個(gè)線程去執(zhí)行里面的方法executorService.execute(() -> {try {// 獲取客戶端發(fā)送過(guò)來(lái)的輸入流InputStream inputStream = socket.getInputStream();byte[] bytes = new byte[1024];int read = inputStream.read(bytes);// 讀取發(fā)送過(guò)來(lái)的信息并打印if (read != -1) {System.out.println(new String(bytes, 0, read));}} catch (IOException e) {e.printStackTrace();} finally {// 斷開(kāi)通訊try {socket.close();} catch (IOException e) {e.printStackTrace();}}});}}
}
BIO的問(wèn)題
上述寫(xiě)法主要是在服務(wù)端接受到一個(gè)客戶端連接時(shí),就開(kāi)啟一個(gè)線程,然后新建一個(gè)連接專門(mén)處理這個(gè)服務(wù)
可以看下accept代碼
public Socket accept() throws IOException {if (this.isClosed()) {throw new SocketException("Socket is closed");} else if (!this.isBound()) {throw new SocketException("Socket is not bound yet");} else {Socket s = new Socket((SocketImpl)null);this.implAccept(s);return s;}}
可以看到,每次accept就會(huì)新建一個(gè)Socket
因此會(huì)有如下問(wèn)題:
每個(gè)請(qǐng)求都需要?jiǎng)?chuàng)建獨(dú)立的線程,與對(duì)應(yīng)的客戶端進(jìn)行數(shù)據(jù)讀,業(yè)務(wù)處理,然后再數(shù)據(jù)寫(xiě)。
當(dāng)并發(fā)數(shù)較大時(shí),需要?jiǎng)?chuàng)建大量的線程來(lái)處理連接,系統(tǒng)資源占用較大。
連接建立后,如果當(dāng)前線程暫時(shí)沒(méi)有數(shù)據(jù)可讀,則線程就阻塞在讀操作上,造成線程資源浪費(fèi)。
基于上面的問(wèn)題產(chǎn)生了NIO
NIO
簡(jiǎn)述
Java NIO全稱java non-blocking IO,是指JDK提供的新API。從JDK1.4開(kāi)始,提供了一系列改進(jìn)的輸入/輸出的新特性,被統(tǒng)稱為NIO(所以也可稱為New IO),是同步非阻塞的。
NIO相關(guān)類都被放在java.nio包及子包下,并且對(duì)原java.io包中的很多類進(jìn)行改寫(xiě)。
NIO有三大核心部分:
Channel(通道)
Buffer(緩沖區(qū))
Selector(選擇器)
NIO是面向緩沖區(qū)的。數(shù)據(jù)讀取到一個(gè)它的稍后處理的緩沖區(qū),需要時(shí)可以在緩沖區(qū)中前后移動(dòng),這就增加了處理過(guò)程中的靈活性,使用它可以提供非阻塞式的高伸縮性網(wǎng)絡(luò)。
Java NIO的非阻塞模式,是一個(gè)線程從某通道發(fā)送請(qǐng)求或者讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒(méi)有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取,而不是保持線程阻塞,所以直至數(shù)據(jù)變得可以讀取之前,該線程可以繼續(xù)做其他的事情。非阻塞寫(xiě)也是如此,一個(gè)線程請(qǐng)求寫(xiě)入一些數(shù)據(jù)到某通道,但不需要等待它完全寫(xiě)入,這個(gè)線程同時(shí)可以去做別的事情。
通俗理解:NIO是可以做到用一個(gè)線程來(lái)處理多個(gè)操作的。假設(shè)有10000個(gè)請(qǐng)求過(guò)來(lái),根據(jù)實(shí)際情況,可以分配50或者100個(gè)線程來(lái)處理。不像之前的阻塞IO那樣,非得分配10000個(gè)。
HTTP2.0使用了多路復(fù)用的技術(shù),做到了同一個(gè)連接并發(fā)處理多個(gè)請(qǐng)求,而且并發(fā)請(qǐng)求的數(shù)量比HTTP1.1大了好幾個(gè)數(shù)量級(jí)。
Buffer
Buffer(緩沖區(qū)):緩沖區(qū)本質(zhì)上是一個(gè)可以讀寫(xiě)數(shù)據(jù)的內(nèi)存塊,可以理解成是一個(gè)容器對(duì)象(含數(shù)組),該對(duì)象提供了一組方法,可以更輕松的使用內(nèi)存塊,緩沖區(qū)對(duì)象內(nèi)置了一些機(jī)制,能夠跟蹤和記錄緩沖區(qū)的狀態(tài)變化情況。Channel提供從文件、網(wǎng)絡(luò)讀取數(shù)據(jù)的渠道,但是讀取或?qū)懭氲臄?shù)據(jù)都必須經(jīng)由Buffer。
在使用Buffer進(jìn)行數(shù)據(jù)讀寫(xiě)的時(shí)候,主要是通過(guò)底層的這個(gè)數(shù)組來(lái)儲(chǔ)存數(shù)據(jù),但是具體的控制數(shù)據(jù)讀寫(xiě),是通過(guò)父類Buffer中的以下參數(shù)來(lái)控制的:
屬性 | 描述 |
---|---|
Capacity | 容量,即可以容納的最大數(shù)據(jù)量。在緩沖區(qū)被創(chuàng)建時(shí)被確定并且不能改變 |
Limit | 示緩沖區(qū)的當(dāng)前終點(diǎn),不能對(duì)緩沖區(qū)超過(guò)limit的位置進(jìn)行讀寫(xiě)操作,且limit是可以修改的 |
Position | 位置,下一個(gè)要被讀/寫(xiě)的元素的索引,每次讀寫(xiě)緩沖區(qū)數(shù)據(jù)時(shí)都會(huì)改變position的值,為下次讀寫(xiě)做準(zhǔn)備 |
Mark | 標(biāo)記 |
一共有7個(gè)類直接繼承了Buffer類,這7個(gè)子類分別是除了boolean外的其他7中數(shù)據(jù)類型的Buffer類。
在這七個(gè)子類中,都有一個(gè)相應(yīng)數(shù)據(jù)類型的數(shù)組,比如IntBuffer中就有一個(gè)int類型的數(shù)組:
final int[] hb;
在ByteBuffer類中就有一個(gè)byte類型的數(shù)組:
final byte[] hb;
實(shí)例:
package Scoket.client;import java.nio.IntBuffer;public class Buffer {public static void main(String[] args) {// 創(chuàng)建一個(gè)IntBuffer對(duì)象實(shí)例,分配容量為5IntBuffer buffer = IntBuffer.allocate(5);for (int i = 0; i < buffer.capacity(); i++) {// 每次循環(huán)為buffer塞一個(gè)int類型的數(shù)值,經(jīng)過(guò)5次循環(huán)后,buffer中應(yīng)該有0、2、4、6、8這5個(gè)數(shù)buffer.put(i * 2);}// 當(dāng)要將buffer從寫(xiě)入轉(zhuǎn)換到讀取的時(shí)候,需要調(diào)用flip()方法// flip()方法是將limit指向position的位置,并且再將position置0// 表示從頭再讀到調(diào)用flip()方法的地方buffer.flip();// hasRemaining()方法表示是否還有剩余的元素可讀取// 里面是通過(guò)position < limit判斷是否有剩余的元素while (buffer.hasRemaining()) {System.out.println(buffer.get());}// 這時(shí)將position的位置設(shè)置成1,limit的位置設(shè)置成4buffer.position(1);buffer.limit(4);// 因?yàn)椴荒茏x取超過(guò)limit的元素,并且從position位置開(kāi)始讀取,所以這里將會(huì)輸出2、4、6while (buffer.hasRemaining()) {System.out.println(buffer.get());}}
}
Channel(通道)
NIO的通道類似于流,但兩者之間有所區(qū)別:
通道可以同時(shí)進(jìn)行讀寫(xiě),而流只能讀或者只能寫(xiě)
通道可以實(shí)現(xiàn)異步讀寫(xiě)數(shù)據(jù)
通道可以從緩沖區(qū)讀取數(shù)據(jù),也可以寫(xiě)數(shù)據(jù)到緩沖區(qū)
BIO的stream是單向的,例如FileInputStream對(duì)象只能進(jìn)行讀取數(shù)據(jù)的操作,而NIO中的通道(Channel)是雙向的,可以讀操作,也可以寫(xiě)操作。
Channel在NIO中是一個(gè)接口。
常用的Channel類有:FileChannel、DatagramChannel、ServerSocketChannel、SocketChannel。FileChannel用于文件的數(shù)據(jù)讀寫(xiě),DatagramChannel用于UDP的數(shù)據(jù)讀寫(xiě),ServerSocketChannel和SocketChannel用于TCP的數(shù)據(jù)讀寫(xiě)。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class Channel {public static void main(String[] args) throws Exception {// 從桌面上隨機(jī)取一張圖片進(jìn)行復(fù)制操作// 獲取原圖片和被復(fù)制圖片路徑的流FileInputStream fileInputStream = new FileInputStream("/src/main/resources/img.png");FileOutputStream fileOutputStream = new FileOutputStream("/src/main/resources/img_1.png");// 通過(guò)流的getChannel()方法獲取兩個(gè)通道FileChannel fileInputStreamChannel = fileInputStream.getChannel();FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();// 創(chuàng)建一個(gè)字節(jié)類型的緩沖區(qū),并為其分配1024長(zhǎng)度ByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 每次讀取圖片的字節(jié)到緩沖區(qū),當(dāng)讀返回-1時(shí),表示讀完了while (fileInputStreamChannel.read(byteBuffer) > -1) {// 調(diào)用flip()方法,從讀的狀態(tài)變?yōu)閷?xiě)的狀態(tài)byteBuffer.flip();// 復(fù)制,將緩沖區(qū)中的數(shù)據(jù)寫(xiě)入到管道中fileOutputStreamChannel.write(byteBuffer);// 將緩沖區(qū)清空,以便于下一次讀取byteBuffer.clear();}// 關(guān)閉Closeable對(duì)象fileOutputStreamChannel.close();fileInputStreamChannel.close();fileOutputStream.close();fileInputStream.close();}
}
Selector(選擇器)
基本介紹
Java的NIO,用非阻塞的IO方式。可以用一個(gè)線程,處理多個(gè)的客戶端連接,就會(huì)使用到Selector(選擇器)。
Selector能夠檢測(cè)多個(gè)注冊(cè)的通道上是否有事件發(fā)生,如果有事件發(fā)生,便獲取時(shí)間然后針對(duì)每個(gè)事件進(jìn)行相應(yīng)的處理。這樣就可以只用一個(gè)單線程去管理多個(gè)通道,也就是管理多個(gè)連接和請(qǐng)求。
只有在連接通道真正有讀寫(xiě)事件發(fā)生時(shí),才會(huì)進(jìn)行讀寫(xiě),就大大地減少了系統(tǒng)開(kāi)銷,并且不必為每一個(gè)連接都創(chuàng)建一個(gè)線程,不用去維護(hù)多個(gè)線程。避免了多個(gè)線程之間的上下文切換導(dǎo)致的開(kāi)銷
SelectionKey為Selector中,有一個(gè)Channel注冊(cè)了,就會(huì)生成一個(gè)SelectionKey對(duì)象,在同步非阻塞中,Selector可以通過(guò)SelectionKey找到相應(yīng)的Channel并處理。
SelectionKey在Selector和Channel的注冊(cè)關(guān)系中一共分為四種:
Int OP_ACCEPT:有新的網(wǎng)絡(luò)連接可以accept,值為16(1<<4)
int OP_CONNECT:代表連接已經(jīng)建立,值為8(1<<3)
int OP_WRITE:代表寫(xiě)操作,值為4(1<<2)
int OP_READ:代表讀操作,值為1(1<<0)
使用實(shí)例
客戶端:
package Scoket.client;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 {// 連接服務(wù)器SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 6443));// 發(fā)送數(shù)據(jù)String message = "Hello, Server!";ByteBuffer buffer = ByteBuffer.wrap(message.getBytes("UTF-8"));socketChannel.write(buffer);System.out.println("Sent to server: " + message);// 關(guān)閉連接socketChannel.close();}
}
服務(wù)端
package Scoket.client;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.Iterator;
import java.util.Set;public class NioServer {public static void main(String[] args) throws IOException {// 打開(kāi) SelectorSelector selector = Selector.open();// 打開(kāi) ServerSocketChannel,監(jiān)聽(tīng)客戶端連接ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(6443));// 設(shè)置為非阻塞模式serverSocketChannel.configureBlocking(false);// 注冊(cè)接受連接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server listening on port 6443");while (true) {// 阻塞直到有就緒事件發(fā)生int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 獲取就緒事件的 SelectionKey 集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {// 有新的連接handleAccept(key, selector);} else if (key.isReadable()) {// 有數(shù)據(jù)可讀handleRead(key);}keyIterator.remove();}}}private static void handleAccept(SelectionKey key, Selector selector) throws IOException {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);// 注冊(cè)讀事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("Accepted connection from: " + socketChannel.getRemoteAddress());}private static void handleRead(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[bytesRead];buffer.get(data);System.out.println("Received from client: " + new String(data, "UTF-8"));// 在這里可以添加業(yè)務(wù)邏輯,然后將響應(yīng)數(shù)據(jù)寫(xiě)入到 SocketChannel// ...// 關(guān)閉連接socketChannel.close();}}
}
參考源:
https://zhuanlan.zhihu.com/p/462497498
https://blog.csdn.net/a78270528/article/details/80318571
https://juejin.cn/post/6924670437867651080
https://juejin.cn/post/6925046428213608456