初識NIO之Java小Demo

Java中的IO、NIO、AIO:

BIO:在Java1.4之前,我們建立網絡連接均使用BIO,屬于同步阻塞IO。默認情況下,當有一條請求接入就有一條線程專門接待。所以,在客戶端向服務端請求時,會詢問是否有空閑線程進行接待,如若沒有則一直等待或拒接。當并發量小時還可以接受,當請求量一多起來則會有許多線程生成,在Java中,多線程的上下文切換會消耗計算機有限的資源和性能,造成資源浪費。

NIO:NIO的出現是為了解決再BIO下的大并發量問題。其特點是能用一條線程管理所有連接。如下圖所示:

NIO是同步非阻塞模型,通過一條線程控制選擇器(Selector)來管理多個Channel,減少創建線程和上下文切換的浪費。當線程通過選擇器向某條Channel請求數據但其沒有數據時,是不會阻塞的,直接返回,繼續干其他事。而一旦某Channel就緒,線程就能去調用請求數據等操作。當該線程對某條Channel進行寫操作時同樣不會被阻塞,所以該線程能夠對多個Channel進行管理。

NIO是面向緩沖流的,即數據寫入寫出都是通過 Channel —— Buffer 這一途徑。(雙向流通)

AIO:與之前兩個IO模型不同的是,AIO屬于異步非阻塞模型。當進行讀寫操作時只須調用api的read方法和write方法,這兩種方法均是異步。對于讀方法來說,當有流可讀取時,操作系統會將可讀的流傳入read方法的緩沖區,并通知應用程序;對于寫操作而言,當操作系統將write方法傳遞的流寫入完畢時,操作系統主動通知應用程序。換言之就是當調用完api后,操作系統完成后會調用回調函數。

總結:一般IO分為同步阻塞模型(BIO),同步非阻塞模型(NIO),異步阻塞模型,異步非阻塞模型(AIO)

同步阻塞模型指的是當調用io操作時必須等到其io操作結束

同步非阻塞模型指當調用io操作時不必等待可以繼續干其他事,但必須不斷詢問io操作是否完成。

異步阻塞模型指應用調用io操作后,由操作系統完成io操作,但應用必須等待或去詢問操作系統是否完成。

異步非阻塞指應用調用io操作后,由操作系統完成io操作并調用回調函數,應用完成放手不管。

NIO的小Demo之服務端

首先,先看下服務端的大體代碼

public class ServerHandle implements Runnable{//帶參數構造函數public ServerHandle(int port){}//停止方法public void shop(){}//寫方法private void write(SocketChannel socketChannel, String  response)throws IOException{}//當有連接進來時的處理方法private void handleInput(SelectionKey key) throws IOException{} //服務端運行主體方法@Overridepublic void run() {}
}
復制代碼

首先我們先看看該服務端的構造函數的實現:

public ServerHandle(int port){try {//創建選擇器selector = Selector.open();//打開監聽通道serverSocketChannel = ServerSocketChannel.open();//設置為非阻塞模式serverSocketChannel.configureBlocking(false);//傳入端口,并設定連接隊列最大為1024serverSocketChannel.socket().bind(new InetSocketAddress(port),1024);//監聽客戶端請求serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//標記啟動標志started = true;System.out.println("服務器已啟動,端口號為:" + port);} catch (IOException e){e.printStackTrace();System.exit(1);}}
復制代碼

在這里創建了選擇器和監聽通道,并將該監聽通道注冊到選擇器上并選擇其感興趣的事件(accept)。后續其他接入的連接都將通過該 監聽通道 傳入。

然后就是寫方法的實現:

    private void doWrite(SocketChannel channel, String response) throws IOException {byte[] bytes = response.getBytes();ByteBuffer wirteBuffer = ByteBuffer.allocate(bytes.length);wirteBuffer.put(bytes);//將寫模式改為讀模式wirteBuffer.flip();//寫入管道channel.write(wirteBuffer);}
復制代碼

其次是當由事件傳入時,即對連接進來的鏈接的處理方法

    private void handleInput(SelectionKey key) throws IOException{//當該鍵可用時if (key.isValid()){if (key.isAcceptable()){//返回該密鑰創建的通道。ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();通過該通道獲取鏈接進來的通道SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}if (key.isReadable()){//返回該密鑰創建的通道。SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int readBytes = socketChannel.read(byteBuffer);if (readBytes > 0){byteBuffer.flip();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);String expression = new String(bytes, "UTF-8");System.out.println("服務器收到的信息:" + expression);//此處是為了區別打印在工作臺上的數據是由客戶端產生還是服務端產生doWrite(socketChannel, "+++++" + expression + "+++++");} else if(readBytes == 0){//無數據,忽略}else if (readBytes < 0){//資源關閉key.cancel();socketChannel.close();}}}}
復制代碼

這里要說明的是,只要ServerSocketChannel及SocketChannel向Selector注冊了特定的事件,Selector就會監控這些事件是否發生。 如在構造方法中有一通道serverSocketChannel注冊了accept事件。當其就緒時就可以通過調用selector的selectorKeys()方法,訪問”已選擇鍵集“中的就緒通道。

壓軸方法:

    @Overridepublic void run() {//循環遍歷while (started) {try {//當沒有就緒事件時阻塞selector.select();//返回就緒通道的鍵Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> iterator = keys.iterator();SelectionKey key;while (iterator.hasNext()){key = iterator.next();//獲取后必須移除,否則會陷入死循環iterator.remove();try {//對就緒通道的處理方法,上述有描述handleInput(key);} catch (Exception e){if (key != null){key.cancel();if (key.channel() != null) {key.channel().close();}}}}}catch (Throwable throwable){throwable.printStackTrace();}}}
復制代碼

此方法為服務端的主體方法。大致流程如下:

  1. 打開ServerSocketChannel,監聽客戶端連接
  2. 綁定監聽端口,設置連接為非阻塞模式(阻塞模式下不能注冊到選擇器)
  3. 創建Reactor線程,創建選擇器并啟動線程
  4. 將ServerSocketChannel注冊到Reactor線程中的Selector上,監聽ACCEPT事件
  5. Selector輪詢準備就緒的key
  6. Selector監聽到新的客戶端接入,處理新的接入請求,完成TCP三次握手,簡歷物理鏈路
  7. 設置客戶端鏈路為非阻塞模式
  8. 將新接入的客戶端連接注冊到Reactor線程的Selector上,監聽讀操作,讀取客戶端發送的網絡消息 異步讀取客戶端消息到緩沖區
  9. 調用write將消息異步發送給客戶端

NIO的小Demo之客戶端

public class ClientHandle implements Runnable{//構造函數,構造時順便綁定public ClientHandle(String ip, int port){}//處理就緒通道private void handleInput(SelectionKey key) throws IOException{}//寫方法(與服務端的寫方法一致)private void doWrite(SocketChannel channel,String request) throws IOException{}//連接到服務端private void doConnect() throws IOException{}//發送信息public void sendMsg(String msg) throws Exception{}
}
復制代碼

首先先看構造函數的實現:

    public ClientHandle(String ip,int port) {this.host = ip;this.port = port;try{//創建選擇器selector = Selector.open();//打開監聽通道socketChannel = SocketChannel.open();//如果為 true,則此通道將被置于阻塞模式;如果為 false,則此通道將被置于非阻塞模式socketChannel.configureBlocking(false);started = true;}catch(IOException e){e.printStackTrace();System.exit(1);}}
復制代碼

接下來看對就緒通道的處理辦法:

    private void handleInput(SelectionKey key) throws IOException{if(key.isValid()){SocketChannel sc = (SocketChannel) key.channel();if(key.isConnectable()){//這里的作用將在后面的代碼(doConnect方法)說明if(sc.finishConnect()){System.out.println("已連接事件");}else{System.exit(1);}}//讀消息if(key.isReadable()){//創建ByteBuffer,并開辟一個1k的緩沖區ByteBuffer buffer = ByteBuffer.allocate(1024);//讀取請求碼流,返回讀取到的字節數int readBytes = sc.read(buffer);//讀取到字節,對字節進行編解碼if(readBytes>0){buffer.flip();//根據緩沖區可讀字節數創建字節數組byte[] bytes = new byte[buffer.remaining()];//將緩沖區可讀字節數組復制到新建的數組中buffer.get(bytes);String result = new String(bytes,"UTF-8");System.out.println("客戶端收到消息:" + result);}lse if(readBytes==0){//忽略}else if(readBytes<0){//鏈路已經關閉,釋放資源key.cancel();sc.close();}}}}
復制代碼

在run方法之前需先看下此方法的實現:

    private void doConnect() throws IOException{if(socketChannel.connect(new InetSocketAddress(host,port))){System.out.println("connect");}else {socketChannel.register(selector, SelectionKey.OP_CONNECT);System.out.println("register");}}
復制代碼

當SocketChannel工作于非阻塞模式下時,調用connect()時會立即返回: 如果連接建立成功則返回的是true(比如連接localhost時,能立即建立起連接),否則返回false。

在非阻塞模式下,返回false后,必須要在隨后的某個地方調用finishConnect()方法完成連接。 當SocketChannel處于阻塞模式下時,調用connect()時會進入阻塞,直至連接建立成功或者發生IO錯誤時,才從阻塞狀態中退出。

所以該代碼在connect服務端后返回false(但還是有作用的),并在else語句將該通道注冊在選擇器上并選擇connect事件。

客戶端的run方法:

    @Overridepublic void run() {try{doConnect();}catch(IOException e){e.printStackTrace();System.exit(1);}//循環遍歷selectorwhile(started){try{selector.select();Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> it = keys.iterator();SelectionKey key ;while(it.hasNext()){key = it.next();it.remove();try{handleInput(key);}catch(Exception e){if(key != null){key.cancel();if(key.channel() != null){key.channel().close();}}}}}catch(Exception e){e.printStackTrace();System.exit(1);}}//selector關閉后會自動釋放里面管理的資源if(selector != null){try{selector.close();}catch (Exception e) {e.printStackTrace();}}}
復制代碼

發送信息到服務端的方法:

    public void sendMsg(String msg) throws Exception{//覆蓋其之前感興趣的事件(connect),將其更改為OP_READsocketChannel.register(selector, SelectionKey.OP_READ);doWrite(socketChannel, msg);}
復制代碼

完整代碼:

服務端:

/*** Created by innoyiya on 2018/8/20.*/
public class Service {private static int DEFAULT_POST = 12345;private static ServerHandle serverHandle;public static void start(){start(DEFAULT_POST);}public static synchronized void start(int post) {if (serverHandle != null){serverHandle.shop();}serverHandle = new ServerHandle(post);new Thread(serverHandle,"server").start();}
}
復制代碼

服務端主體:

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;/*** Created by innoyiya on 2018/8/20.*/
public class ServerHandle implements Runnable{private Selector selector;private ServerSocketChannel serverSocketChannel;private volatile boolean started;public ServerHandle(int port){try {//創建選擇器selector = Selector.open();//打開監聽通道serverSocketChannel = ServerSocketChannel.open();//設置為非阻塞模式serverSocketChannel.configureBlocking(false);//判定端口,并設定連接隊列最大為1024serverSocketChannel.socket().bind(new InetSocketAddress(port),1024);//監聽客戶端請求serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//標記啟動標志started = true;System.out.println("服務器已啟動,端口號為:" + port);} catch (IOException e){e.printStackTrace();System.exit(1);}}public void shop(){started = false;}private void doWrite(SocketChannel channel, String response) throws IOException {byte[] bytes = response.getBytes();ByteBuffer wirteBuffer = ByteBuffer.allocate(bytes.length);wirteBuffer.put(bytes);wirteBuffer.flip();channel.write(wirteBuffer);}private void handleInput(SelectionKey key) throws IOException{if (key.isValid()){if (key.isAcceptable()){ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}if (key.isReadable()){SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int readBytes = socketChannel.read(byteBuffer);if (readBytes > 0){byteBuffer.flip();byte[] bytes = new byte[byteBuffer.remaining()];byteBuffer.get(bytes);String expression = new String(bytes, "UTF-8");System.out.println("服務器收到的信息:" + expression);doWrite(socketChannel, "+++++" + expression + "+++++");} else if (readBytes < 0){key.cancel();socketChannel.close();}}}}@Overridepublic void run() {//循環遍歷while (started) {try {selector.select();//System.out.println(selector.select());Set<SelectionKey> keys = selector.selectedKeys();//System.out.println(keys.size());Iterator<SelectionKey> iterator = keys.iterator();SelectionKey key;while (iterator.hasNext()){key = iterator.next();iterator.remove();try {handleInput(key);} catch (Exception e){if (key != null){key.cancel();if (key.channel() != null) {key.channel().close();}}}}}catch (Throwable throwable){throwable.printStackTrace();}}}
}
復制代碼

客戶端:

/*** Created by innoyiya on 2018/8/20.*/
public class Client {private static String DEFAULT_HOST = "localhost";private static int DEFAULT_PORT = 12345;private static ClientHandle clientHandle;private static final String EXIT = "exit";public static void start() {start(DEFAULT_HOST, DEFAULT_PORT);}public static synchronized void start(String ip, int port) {if (clientHandle != null){clientHandle.stop();}clientHandle = new ClientHandle(ip, port);new Thread(clientHandle, "Server").start();}//向服務器發送消息public static boolean sendMsg(String msg) throws Exception {if (msg.equals(EXIT)){return false;}clientHandle.sendMsg(msg);return true;}}
復制代碼

客戶端主體代碼:

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.util.Iterator;
import java.util.Set;/*** Created by innoyiya on 2018/8/20.*/public class ClientHandle implements Runnable{private String host;private int port;private Selector selector;private SocketChannel socketChannel;private volatile boolean started;public ClientHandle(String ip,int port) {this.host = ip;this.port = port;try{//創建選擇器selector = Selector.open();//打開監聽通道socketChannel = SocketChannel.open();//如果為 true,則此通道將被置于阻塞模式;如果為 false,則此通道將被置于非阻塞模式socketChannel.configureBlocking(false);started = true;}catch(IOException e){e.printStackTrace();System.exit(1);}}public void stop(){started = false;}private void handleInput(SelectionKey key) throws IOException{if(key.isValid()){SocketChannel sc = (SocketChannel) key.channel();if(key.isConnectable()){if(sc.finishConnect()){System.out.println("已連接事件");}else{System.exit(1);}}//讀消息if(key.isReadable()){//創建ByteBuffer,并開辟一個1M的緩沖區ByteBuffer buffer = ByteBuffer.allocate(1024);//讀取請求碼流,返回讀取到的字節數int readBytes = sc.read(buffer);//讀取到字節,對字節進行編解碼if(readBytes>0){//將緩沖區當前的limit設置為position=0,用于后續對緩沖區的讀取操作buffer.flip();//根據緩沖區可讀字節數創建字節數組byte[] bytes = new byte[buffer.remaining()];//將緩沖區可讀字節數組復制到新建的數組中buffer.get(bytes);String result = new String(bytes,"UTF-8");System.out.println("客戶端收到消息:" + result);} else if(readBytes<0){key.cancel();sc.close();}}}}//異步發送消息private void doWrite(SocketChannel channel,String request) throws IOException{byte[] bytes = request.getBytes();ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);writeBuffer.put(bytes);//flip操作writeBuffer.flip();//發送緩沖區的字節數組channel.write(writeBuffer);}private void doConnect() throws IOException{if(socketChannel.connect(new InetSocketAddress(host,port))){System.out.println("connect");}else {socketChannel.register(selector, SelectionKey.OP_CONNECT);System.out.println("register");}}public void sendMsg(String msg) throws Exception{//覆蓋其之前感興趣的事件,將其更改為OP_READsocketChannel.register(selector, SelectionKey.OP_READ);doWrite(socketChannel, msg);}@Overridepublic void run() {try{doConnect();}catch(IOException e){e.printStackTrace();System.exit(1);}//循環遍歷selectorwhile(started){try{selector.select();Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> it = keys.iterator();SelectionKey key ;while(it.hasNext()){key = it.next();it.remove();try{handleInput(key);}catch(Exception e){if(key != null){key.cancel();if(key.channel() != null){key.channel().close();}}}}}catch(Exception e){e.printStackTrace();System.exit(1);}}//selector關閉后會自動釋放里面管理的資源if(selector != null){try{selector.close();}catch (Exception e) {e.printStackTrace();}}}
}
復制代碼

測試類:

import java.util.Scanner;/*** Created by innoyiya on 2018/8/20.*/
public class Test {public static void main(String[] args) throws Exception {Service.start();Thread.sleep(1000);Client.start();while(Client.sendMsg(new Scanner(System.in).nextLine()));}
}
復制代碼

控制臺打印:

服務器已啟動,端口號為:12345
register
已連接事件
1234
服務器收到的信息:1234
客戶端收到消息:+++++1234+++++
5678
服務器收到的信息:5678
客戶端收到消息:+++++5678+++++
復制代碼

如有不妥之處,請告訴我。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450866.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450866.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450866.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RTP協議詳解

RTP協議分析 第1章. RTP概述 1.1. RTP是什么 RTP全名是Real-time Transport Protocol&#xff08;實時傳輸協議&#xff09;。它是IETF提出的一個標準&#xff0c;對應的RFC文檔為RFC3550&#xff08;RFC1889為其過期版本&#xff09;。RFC3550不僅定義了RTP&#xff0…

線程狀態轉換

一、線程狀態轉換 新建&#xff08;New&#xff09; 創建后尚未啟動。 可運行&#xff08;Runnable&#xff09; 可能正在運行&#xff0c;也可能正在等待 CPU 時間片。 包含了操作系統線程狀態中的 Running 和 Ready。 阻塞&#xff08;Blocking&#xff09; 等待獲取一個排它…

Eclipse中啟動tomcat報錯java.lang.OutOfMemoryError: PermGen space的解決方法

見&#xff1a;http://outofmemory.cn/java/OutOfMemoryError/outofmemoryerror-permgen-space-in-tomcat-with-eclipse 有的項目引用了太多的jar包&#xff0c;或者反射生成了太多的類&#xff0c;異或有太多的常量池&#xff0c;就有可能會報java.lang.OutOfMemoryError: Per…

MPEG-4 AVC/H.264 信息

作者&#xff1a;haibara 來源&#xff1a;pcicp.com 本FAQ由&#xff08;haibara&#xff09;翻譯&#xff0c;期間受到kaito_mkid&#xff08;pcicp&#xff09;幫助&#xff0c;在此感謝&#xff0c;由于Newbie的關系&#xff0c;如有翻譯錯誤&#xff0c;還請各位指出&…

eclipse搜索關鍵字

見&#xff1a;https://jingyan.baidu.com/article/e6c8503c1a60d2e54f1a18e3.html

裝飾器語法糖運用

裝飾器語法糖運用 前言&#xff1a;函數名是一個特性的變量&#xff0c;可以作為容器的元素&#xff0c;也可以作為函數的參數&#xff0c;也可以當做返回值。閉包定義&#xff1a; 內層函數對外層函數&#xff08;非全局&#xff09;變量的引用&#xff0c;這個內層函數就可以…

fb 4.7英文版 顯示行數

窗口&#xff08;window&#xff09;首選項&#xff08;Preference&#xff09;—>常規&#xff08;General&#xff09;—>編輯器&#xff08;Editors&#xff09;—>文本編輯器&#xff08;Text Editors&#xff09;—>“顯示行號”&#xff08;Show line number…

集市中迷失的一代:FreeBSD核心開發者反思開源軟件質量

摘要&#xff1a;本文作者Poul-Henning Kamp (phkFreeBSD.org) &#xff0c;26年的計算機程序員&#xff0c;他編寫的軟件以底層構建塊的形式廣泛被開源和商業產品采用。講述作者在看完《設計原本》這本書后所引發的共鳴&#xff01; 13年前&#xff0c;新興的草根開源軟件運動…

點擊表格彈窗獲取另外一套數據之后,原表格相關數據的調用

用H5新屬性&#xff0c;data-*&#xff0c; $獲取方式&#xff1a; 待續。。。。。。。 轉載于:https://www.cnblogs.com/He-tao-yuan/p/9888316.html

谷歌瀏覽器如何如何禁用彈出窗口阻止程序

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 在工具欄上使用 Google Chrome 菜單。工具欄上的菜單位于瀏覽器右上角。 選擇“設置”。 在頁面底端找到并點擊“顯示高級設置”。 在“隱…

Python 3 入門,看這篇就夠了

文章目錄 簡介基礎語法運算符變量數據類型流程控制迭代器生成器函數 自定義函數參數傳遞 可更改與不可更改對象參數匿名函數變量作用域模塊面向對象錯誤和異常文件操作序列化命名規范參考資料簡介 Python 是一種高層次的結合了解釋性、編譯性、互動性和面向對象的腳本語言。Pyt…

面試經歷(二)

前面說到用數據庫中的鎖機制對并發事務進行控制&#xff0c;這節來說說事務方法和事務方法發生嵌套調用時事務如何進行傳播。例如&#xff1a;方法可能繼續在現有事務中運行&#xff0c;也可能開啟一個新事務&#xff0c;并在自己的事務中運行。例如&#xff1a;方法可能繼續在…

最有價值的編程忠告

摘要&#xff1a;本文是來自貝爾實驗室Plan 9操作系統的創始人Rob Pike給大家分享的編程忠告&#xff01;Rob Pike&#xff0c;目前谷歌公司最著名的軟件工程師之一&#xff0c;曾是貝爾實驗室Unix開發團隊成員&#xff0c;締造Go語言和Limbo語言的核心人物。 Rob Pike&#xf…

Column count doesn't match value count at row 1 原因

mysql 提示 &#xff1a; Column count doesnt match value count at row 1錯誤&#xff0c;SQL語句中列的個數和值的個數不等&#xff0c; 如&#xff1a; insert into table1 (field1,field2) values(值1&#xff0c;值2&#xff0c;值3 ) 列只有2個&#xff0c;值 卻有3個…

MarkDowm快捷鍵大全

文章目錄一&#xff1a;菜單欄二&#xff1a;文件三&#xff1a;編輯四&#xff1a;段落五&#xff1a;格式六&#xff1a;視圖一&#xff1a;菜單欄 文件&#xff1a;altF 編輯&#xff1a;altE 段落&#xff1a;altP 格式&#xff1a;altO 視圖&#xff1a;altV 主題&#x…

Kinect2.0-空間長度測量

1. 鼠標左鍵按下選擇起點&#xff0c;拖動鼠標&#xff0c;左鍵放開&#xff0c;確定終點。 實現效果1實現效果22. 在linux下使用libfreenect2開源多平臺驅動來獲取kinect2.0的傳感器信息&#xff0c;得到深度信息&#xff0c;并通過libfreenect2提供的getPointXYZ函數&#xf…

帶數據庫的智能合約

編寫使用數據庫的智能合約 前面一直在搗鼓EOS網絡搭建的相關東西。然而今天比較不走運的是&#xff0c;興致勃勃的把源碼版本升到4.0&#xff0c;在編譯的時候如我所猜想的出現了一系列問題&#xff0c;正一籌莫展的時候&#xff0c;導師突然問了我一個關于合約如何操作數據庫的…

沒有完美的軟件:編程永遠不容易

摘要&#xff1a;很多人想用十全十美來修飾一樣東西&#xff0c;比如軟件&#xff0c;對于客戶來說&#xff0c;當然希望他們的軟件能做到完美。雖然很多專家說利用一些規范可以讓軟件達到更好&#xff01;但是在現實開發中&#xff0c;真的會有那么完美的軟件嗎&#xff1f; 最…

Eclipse斷點調試出現Source not found

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 我的情況是和網上說的都不一樣&#xff0c;我真的也沒有想到這么坑&#xff0c; 我居然無意寫了一個死循環&#xff0c;dao/mapper調sql…

Memory Compression這到底是個什么東西?

Memory Compression這到底是個什么東西&#xff1f; Memory Compression這個進程&#xff0c;經過我的查詢說是內存壓縮功能&#xff0c;作用是壓縮內存讓內存占用更低&#xff0c;但是為什么這個進程瘋狂占用我的內存&#xff0c;我用的Win10 8G&#xff0c;通過資源監視器查看…