查看這篇文章,了解更多關於Java的阻塞和非阻塞替代創建套接字的信息。
套接字使用TCP / IP傳輸協議,是兩台主機之間的最后一塊網絡通信。 您通常不必處理它們,因為它們之上構建了協議,如HTTP或FTP; 但是,了解它們的工作方式非常重要。
TCP:它是一種可靠的數據傳輸協議,可確保發送的數據完整且正確,並且需要建立連接。Java提供了一種阻塞和非阻塞替代方法來創建套接字,並且根據您的要求,您可以考慮使用其中一個。
Java IO
Java阻塞IO API包含在Java.net包下的JDK中,通常最簡單易用。
此API基於可以讀取或寫入的字節流和字符流。 沒有可用於前后移動的索引,就像在數組中一樣,它只是一個連續的數據流。
每次客戶端請求連接到服務器時,它都會阻塞一個線程。 因此,如果我們希望有許多同時連接,我們必須創建足夠大的線程池。
1. 使用給定端口創建ServerSocket以進行偵聽。
2. 當調用accept()並開始監聽客戶端連接時,服務器將阻塞。
3. 如果客戶端請求連接,則accept()返回一個Socket。
4. 現在,我們可以從客戶端(InputStream)讀取並將數據發送回客戶端(OutputStream)。
如果我們想允許多個連接,我們必須創建一個線程池:
如您所見,此API有一些限制。 我們將無法接受比機器中可用線程更多的連接。 因此,如果您希望有許多連接,則需要一個替代方案。
Java NIO
Java.nio是用於套接字連接的非阻塞API,這意味着您對可用線程的數量並不緊密。 使用此庫,一個線程可以同時處理多個連接。
Channel::通道是輸入和輸出流的組合,因此它們允許您進行讀寫,並且它們使用緩沖區來執行這些操作。
Buffer:它是一塊內存,用於從通道讀取並寫入其中。 當你想從Buffer中讀取數據時,需要調用flip(),以便將pos設置為0。
1. 在第1行,pos將等於寫入Buffer的字節數。
2. 在第3行,調用flip()將位置設置為0並限制先前寫入的字節數。
3. 在第5行,它一次從緩沖區讀取一個字節到極限。
4. 最后,在第7行,我們清除緩沖區。
Selector:選擇器可以注冊多個通道,並檢查哪些通道已准備好接受新連接。 與阻塞IO的accept()方法類似,當調用select()時,它將阻塞應用程序,直到Channel准備好進行操作。 由於Selector可以注冊多個通道,因此只需要一個線程來處理多個連接。
Selection Key:它包含特定通道的屬性(興趣集,就緒集,選擇器/通道和可選的附加對象)。 選擇鍵主要用於了解通道的當前興趣(isAcceptable(),isReadable(),isWritable()),獲取通道並對該通道進行操作。
下面我們將使用Echo Socket Channel服務器來展示NIO的工作原理。
1. 從第1行到第3行,創建了ServerSocketChannel,您必須明確地將其設置為非阻塞模式。 套接字還配置為監聽端口8080。
2. 在第5行和第6行,創建一個Selector,並在Selector上注冊ServerSocketChannel,其中SelectionKey指向ACCEPT操作。
3. 為了使應用程序始終保持監聽,阻塞方法select()位於無限while循環內,select()將在選擇至少一個通道時返回,喚醒喚醒()或線程被中斷。
4. 然后,在第10行,從選擇器返回一組鍵,我們將遍歷它們以執行就緒通道。
1. 每次創建新連接時,isAcceptable()都將為true,並且新的Channel將注冊到Selector中。
2. 為了跟蹤每個通道的數據,將它放在一個Map中,其中套接字通道作為鍵和ByteBuffers列表。
3. 然后,選擇器將指向READ操作。
1. 在讀取塊中,將檢索通道,並將傳入的數據寫入ByteBuffer。
2. 在第6行,我們檢查連接是否已關閉。
3. 在第9行和第10行,緩沖區設置為flip()讀取模式並添加到Map。
4. 然后,調用interestOps()以指向WRITE操作。
1. 再一次,檢索通道以便將保存在Map中的數據寫入其中。
2. 然后,我們將Selector設置為READ操作。
如果連接關閉,通道將從Map中刪除,我們關閉通道。
Java IO Vs. NIO
IO和NIO之間的選擇取決於用例。 對於更少的連接和簡單的解決方案,IO可能更適合您。 然而,如果你想要能夠同時處理數千個連接的更高效的東西,NIO可能是更好的選擇,但請記住它會引入很多代碼復雜性。 但是,有一些框架,如Netty或Apache MINA,它們構建在NIO之上,並隱藏了編程的復雜性。
如JAVA上遇到什么不懂的,可以私聊我!