網上有很多關于pos機讓連接tcp是什么,java 中的 NIO 和 IO 到底是什么區(qū)別的知識,也有很多人為大家解答關于pos機讓連接tcp是什么的問題,今天pos機之家(m.afbey.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
pos機讓連接tcp是什么
此賬號為華為云開發(fā)者社區(qū)官方運營賬號,提供全面深入的云計算前景分析、豐富的技術干貨、程序樣例,分享華為云前沿資訊動態(tài)
本文分享自華為云社區(qū)《java中的NIO和IO到底是什么區(qū)別?20個問題告訴你答案【奔跑吧!JAVA】》,原文作者:breakDraw 。
NIO 即 New IO,這個庫是在 JDK1.4 中才引入的。NIO 和 IO 有相同的作用和目的,但實現(xiàn)方式不同,NIO 主要用到的是塊,所以 NIO 的效率要比 IO 高很多。
Q: NIO 和標準 IO 有什么區(qū)別?
A:
標準 IO, 基于字節(jié)流和字符流進行操作,阻塞 IO。NIO 基于通道 channel 和緩沖區(qū) Buffer 進行操作,支持非阻塞 IO,提供選擇器JavaNIO 核心 3 組件:Channels 通道Q: 通道 Channel 對象能同時做讀寫操作嗎?還是說需要像標準 IO 那樣,需要同時創(chuàng)建 input 和 output 對象才能做讀寫操作?
A:通道 Channel 是雙向的, 既可以從 channel 中讀數(shù)據,也可以寫數(shù)據。可以看到既能調用 read 也能調用 write,且需要依賴緩沖區(qū) buffer。
FileChannel fileChannel = FileChannel.open(new File("a.txt").toPath()); ByteBuffer buf = ByteBuffer.allocate(1024); fileChannel.read(buf); fileChannel.write(buf);注意上圖上,fileChannel.read(buf)是將 a.txt 里的數(shù)據讀到 buf, 即 a.txt->buffileChannel.write(buf)是將 buf 里的數(shù)據寫入到 a.txt 中, 即 buf->a.txt,不要搞反啦!通道和緩沖區(qū)的關系
Q: 通道支持異步讀寫嗎
A:支持。
Q: 通道的讀寫是否必須要依賴緩沖區(qū) buffer?
A: 一般都是依賴 buffer 的。 但也支持 2 個管道之間的傳輸,即管道之間直接讀寫。
String[] arr=new String[]{"a.txt","b.txt"};FileChannel in=new FileInputStream(arr[0]).getChannel();FileChannel out =new FileOutputStream(arr[1]).getChannel(); // 將a.txt中的數(shù)據直接寫進b.txt中,相當于文件拷貝in.transferTo(0, in.size(), out);
常用的幾種 Channel
FileChannelJava NIO 中的 FileChannel 是一個連接到文件的通道??梢酝ㄟ^文件通道讀寫文件。FileChannel 無法設置為非阻塞模式,它總是運行在阻塞模式下
創(chuàng)建方式
RandomAccessFile file = new RandomAccessFile("D:/aa.txt");FileChannel fileChannel = file.getChannel();SocketChannel
Java NIO 中的 SocketChannel 是一個連接到 TCP 網絡套接字的通道。
支持非阻塞模式 socketChannel.configureBlocking(false)。
可以通過以下 2 種方式創(chuàng)建 SocketChannel:
打開一個 SocketChannel 并連接到互聯(lián)網上的某臺服務器。
一個新連接到達 ServerSocketChannel 時,會創(chuàng)建一個 SocketChannel
創(chuàng)建方式
SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("192.168.1.100",80));ServerSocketChannel
Java NIO 中的 ServerSocketChannel 是一個可以監(jiān)聽新進來的 TCP 連接的通道, 就像標準 IO 中的 ServerSocket 一樣。
ServerSocketChannel 類在 java.nio.channels 包中。
SocketChannel 和 ServerSocketChannel 的區(qū)別: 前者用于客戶端,后者用于服務端
創(chuàng)建方式:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket.bind(new InetSocketAddress(80));serverSocketChannel.configureBlocking(false);while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannle != null) doSomething...}Buffer 緩沖區(qū)我們真正要把數(shù)據拿到或者要寫數(shù)據, 實際上都是通過 buffer 進行操作的。文件 <-> buffer<-> 數(shù)據buffer 是 1 個即可讀也可寫的緩沖區(qū),擁有讀寫 2 種模式。buffer 的 capacity 屬性限定了每個 buffer 的最大容量,下面的 1024 就是 capacity。
ByteBuffer buf = ByteBuffer.allocate(1024);
buffer 擁有 1 個 position 屬性,表示當前的讀寫位置。往 buffer 中寫數(shù)據時,position 就會增加。position 最大值為 capacity-1把 fileChannel 對應文件里的數(shù)據 寫入到 buffer,叫做寫模式寫之后,調用 flip,讓 buffer 的 postion 置 0,此時相當于準備讀取 buffer 里的數(shù)據(即調用 buffer.get()拿數(shù)據)(這個模式的叫法個人也覺得不太好,很容易繞,你可以就記憶成: flip 就是從寫模式轉成讀模式?。?/p>
Q: buffer 調用 flip()方法從寫模式切換到讀模式時,position 會變成多少?
A: 變?yōu)?0。
ByteBuffer buf = ByteBuffer.allocate(1024); // 數(shù)據讀到buf中,并返回數(shù)量,每次最多讀1024個 int byteRead = fileChannel.read(buf); // 輸出byteRead的數(shù)量,最多為1024 System.out.println("position=" + buf.position()+", byteRead=" + byteRead); buf.flip(); // 切換到讀模式了,輸出0 System.out.println("position=" + buf.position());buffer 擁有 1 個 limit 屬性。寫模式下,buffer 的 limit 就是 buffer 的 capacity。
Q: 當 buffer 從寫模式切換到讀模式時,limit 為多少?
A: 每次切換前都要調用 flip(),切換后,limit 為寫模式中的 position。
int byteRead = fileChannel.read(buf); // 輸出1024 System.out.println("limit=" + buf.limit() + ",postion=" + buf.position()); System.out.println("切換到讀模式"); buf.flip(); // 輸出byteRead數(shù)量 System.out.println("limit=" + buf.limit());
結果如下
Q: 向 buf 緩沖區(qū)寫數(shù)據的方式有哪些?
A:
int byteRead = fileChannel.read(buf);從通道中讀數(shù)據到 buf 中, 即相當于向 buf 緩沖區(qū)中寫數(shù)據。
buf.putChar(‘a’);手動向 buf 中寫入字符 a, postion 加 1。
Q: 從 buf 緩沖區(qū)讀數(shù)據的方式有哪些?
int bytesWrite = fileChannel.write(buf)buf 中的數(shù)據寫入到管道,即相當于 fileChannel 讀取 buf 中的數(shù)據。
byte getByte = buf.get()手動讀取 1 個 buf 中的字符,postion 加 1.
Q: 手動修改當前緩沖區(qū)的 postion 的方法有哪些?
A:
rewind() 將 postion 設置為 0mark() 可以標記 1 個特定的位置, 相當于打標記, 在一頓操作后,可通過 reset()回到之前 mark()的位置(就像你需要 mark 我的這幾篇博文一樣!)Q:1 個 channel 管道支持多個 buffer 嗎?
A: 支持。 通道的 write 和 read 方法都支持傳入 1 個 buffer 數(shù)組,會按照順序做讀寫操作。
Buffer 的種類:
Buffer 的另外 3 個方法:
warp:根據一個 byte[]來生成一個固定的 ByteBuffer 時,使用 ByteBuffer.wrap()非法的合適。他會直接基于 byte[]數(shù)組生成一個新的 buffer,值也保持一致。
slice:得到切片后的數(shù)組。
duplicate:調用 duplicate 方法返回的 Buffer 對象就是復制了一份原始緩沖區(qū),復制了 position、limit、capacity 這些屬性
注意!以上 warp\\slice\\duplicte 生成的緩沖區(qū) get 和 put 所操作的數(shù)組還是與原始緩沖區(qū)一樣的
所以對復制后的緩沖區(qū)進行修改也會修改原始的緩沖區(qū),反之亦然
因此 duplicte、slice 一般是用于操作一下 poistion\\limit 等處理,但是原內容不會去變他,否則就會引起 原緩沖器的修改。
Selectorselector 可用來在線程中關聯(lián)多個通道,并進行事件監(jiān)聽。
Q: 在 NIO 中 Selector 的好處是什么?
A:
可以用更少的線程來管理各個通道。減少線程上下文切換的資源開銷。Q: Selector 支持注冊哪種類型的通道?
A:支持非阻塞的通道。通道要在注冊前調用 channel.configureBlocking(false) 設置為非阻塞。例如 FileChannel 就沒辦法注冊,他注定是阻塞的。而 socketChannel 就可以支持非阻塞。
Q: Selector 注冊時,支持監(jiān)聽哪幾種事件,對應的常量是什么?(啊最不喜歡記憶這種東西了…)
A:共有 4 種可監(jiān)聽事件
Connect 成功連接到 1 個服務器,對應常量 SelectionKey.OP_CONNECTAccept 準備好接收新進入的連接, 對應常量 SelectionKey.OP_ACCEPTRead, 有數(shù)據可讀,對應常量 SelectionKey.OP_READWrite 接收到往里寫的數(shù)據, 對應常量 SelectionKey.OP_WRITE如果希望對該通道監(jiān)聽多種事件,可以用"|"位或操作符把常量連接起來。
int interestingSet = Selectionkey.OP_READ | Selectionkey.OP_WRITE;Selectionkey key = channel.register(selector,interestingSet)SelectionKey 鍵表示了一個特定的通道對象和一個特定的選擇器對象之間的注冊關系
Q: Selector 維護的 SelectionKey 集合共有哪幾種?
A:共有三種。
1. 已注冊的所有鍵的集合(Registered key set)
所有與選擇器關聯(lián)的通道所生成的鍵的集合稱為已經注冊的鍵的集合。并不是所有注冊過的鍵都仍然有效。這個集合通過 keys()方法返回,并且可能是空的。這個已注冊的鍵的集合不是可以直接修改的;試圖這么做的話將引發(fā) java.lang.UnsupportedOperationException。
2. 已選擇的鍵的集合(Selected key set)
已注冊的鍵的集合的子集。這個集合的每個成員都是相關的通道被選擇器(在前一個選擇操作中)判斷為已經準備好的,并且包含于鍵的 interest 集合中的操作。這個集合通過 selectedKeys()方法返回(并有可能是空的)。不要將已選擇的鍵的集合與 ready 集合弄混了。這是一個鍵的集合,每個鍵都關聯(lián)一個已經準備好至少一種操作的通道。每個鍵都有一個內嵌的 ready 集合,指示了所關聯(lián)的通道已經準備好的操作。鍵可以直接從這個集合中移除,但不能添加。試圖向已選擇的鍵的集合中添加元素將拋出 java.lang.UnsupportedOperationException。
3. 已取消的鍵的集合(Cancelled key set)
已注冊的鍵的集合的子集,這個集合包含了 cancel()方法被調用過的鍵(這個鍵已經被無效化),但它們還沒有被注銷。這個集合是選擇器對象的私有成員,因而無法直接訪問。
注冊之后, 如何使用 selector 對準備就緒的通道做處理:
1. 調用 select()方法獲取已就緒的通道,返回的 int 值表示有多少通道已經就緒
2. 從 selector 中獲取 selectedkeys
3. 遍歷 selectedkeys
4. 查看各 SelectionKey 中 是否有事件就緒了。
5. 如果有事件就緒,從 key 中獲取對應對應管道。做對應處理
類似如下,一般都會啟 1 個線程來 run 這個 selector 監(jiān)聽的處理:
while(true) { int readyNum = selector.select(); if (readyNum == 0) { continue; } Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> it = selectedKeys.iterator(); while(it.hasNext()) { SelectionKey key = it.next(); if(key.isAcceptable()) { // 接受連接 } else if (key.isReadable()) { // 通道可讀 } else if (key.isWritable()) { // 通道可寫 } it.remove(); }}
Q:select()方法其實是阻塞方法,即調用時會進入等待,直到把所有通道都輪詢完畢。如果希望提前結束 select(),有哪些方法?
A:有 2 個辦法:
wakeup(), 調用后,select()方法立刻返回。
close(), 直接關閉 selector。
PS: 之前說 NIO 是非阻塞 IO,但為什么上面卻說 select()方法是阻塞的?
其實 NIO 的非阻塞,指的是 IO 不阻塞,即我們不會卡在 read()處,我們會用 selector 去查詢就緒狀態(tài),如果狀態(tài) ok 就。而查詢操作是需要時間,因此 select()必須要把所有通道都檢查一遍才能告訴結果,因此 select 這個查詢操作是阻塞的。其他Q: 多線程讀寫同一文件時,如何加鎖保證線程安全?
A:使用 FileChannel 的加鎖功能。
RandomAccessFile randFile = new RandomAccessFile(target, "rw");FileChannel channel = randFile.getChannel();// pos和siz決定加鎖區(qū)域, shared指定是否是共享鎖FileLock fileLock = channel.lock(pos , size , shared);if (fileLock!=null) {do(); // 這里簡化了,實際上應該用try-catchfileLock.release();}
Q: 如果需要讀 1 個特大文件,可以使用什么緩沖區(qū)?
A:使用 MappedByteBuffer。這個緩沖區(qū)可以把大文件理解成 1 個 byte 數(shù)組來訪問(但實際上并沒有加載這么大的 byte 數(shù)組,實際內容放在內存+虛存中)。主要通過 FileChannel.map(模式,起始位置,區(qū)域)來生成 1 個 MappedByteBuffer。然后可以用 put 和 get 去處理對應位置的 byte。
int length = 0x8FFFFFF;//一個byte占1B,所以共向文件中存128M的數(shù)據try (FileChannel channel = FileChannel.open(Paths.get("src/c.txt"),StandardOpenOption.READ, StandardOpenOption.WRITE);) {MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);for(int i=0;i<length;i++) {mapBuffer.put((byte)0);}for(int i = length/2;i<length/2+4;i++) {//像數(shù)組一樣訪問System.out.println(mapBuffer.get(i));}}
三種模式:
MapMode.READ_ONLY(只讀): 試圖修改得到的緩沖區(qū)將導致拋出 ReadOnlyBufferException。MapMode.READ_WRITE(讀/寫): 對得到的緩沖區(qū)的更改會寫入文件,需要調用 fore()方法MapMode.PRIVATE(專用): 可讀可寫,但是修改的內容不會寫入文件,只是 buffer 自身的改變。Q:NIO 中 ByteBuffer, 該如何根據正確的編碼,轉為對應的 CharBuffer
A:利用 Charset 的 decode 功能。
ByteBuffer byteBuffer = ...;Charset charset = Charset.forName("UTF-8");CharBuffer charBuffer = charset.decode(byteBuffer);
如果是 CharBuffer 轉 ByteBuffer, 就用 charset.encode。
點擊關注,第一時間了解華為云新鮮技術~
以上就是關于pos機讓連接tcp是什么,java 中的 NIO 和 IO 到底是什么區(qū)別的知識,后面我們會繼續(xù)為大家整理關于pos機讓連接tcp是什么的知識,希望能夠幫助到大家!