程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Boost.Asio c++ 網絡編程翻譯(8)

Boost.Asio c++ 網絡編程翻譯(8)

編輯:C++入門知識

Boost.Asio c++ 網絡編程翻譯(8)


TCP VS UDP VS ICMP 就像我之前所說,不是所有的成員方法在所有的套接字類中都可用。我做了一個包含成員函數不同點的列表。如果一個成員函數沒有出現在這,說明它在所有的套接字類都是可用的。 名字 TCP UDP ICMP
async_read_some 是 - -
async_receive_from - 是 是
async_write_some 是 - -
async_send_to - 是 是
read_some 是 - -
receive_from - 是 是
write_some 是 - -
send_to - 是 是
其他方法 其他與連接和I/O無關的函數如下: local_endpoint():這個方法返回套接字本地連接的地址。 remote_endpoint():這個方法返回套接字連接到的遠程地址。 native_handle():這個方法返回原始套接字的處理程序。你只有在調用一個Boost.Asio不支持的原始方法時才需要用到它 non_blocking():如果套接字是非阻塞的,這個方法返回true,否則false
native_non_blocking():如果套接字是非阻塞的,這個方法返回true,否則返回false。但是,它是基於原生的套接字來調用本地的api。所以通常來說,你不需要調用這個方法(non_blocking()已經緩存了這個結果);你只有在直接調用native_handle()這個方法的時候猜需要使用到這個方法 at_mark():如果套接字要讀的是一段OOB數據,這個方法返回true。這個方法你很少會用到
其他的考慮 最後要注意,一個套接字實例不能被拷貝,因為拷貝構造方法和=操作符是不可訪問的。
ip::tcp::socket s1(service), s2(service);
   s1 = s2; // 編譯時報錯
   ip::tcp::socket s3(s1); // 編譯時報錯
這是非常有意義的,因為每一個實例都擁有並管理著一個資源(原生套接字本身)。如果我們允許拷貝構造,結果是我們會有兩個實例擁有同樣的原生套接字;這樣我們就需要去處理所有者的問題(讓一個實例擁有所有權?或者使用引用計數?還是其他的方法)Boost.Asio選擇不允許拷貝(如果你想要創建一個備份,請使用共享指針)
typedef boost::shared_ptr socket_ptr;
   socket_ptr sock1(new ip::tcp::socket(service));
   socket_ptr sock2(sock1); // ok
   socket_ptr sock3;			
   sock3 = sock1; // ok
套接字緩沖區
當從一個套接字讀寫內容時,你需要一個緩沖區,用來保存讀入和寫出的數據。緩沖區內存的有效時間必須比I/O操作的時間要長;你需要保證它們在I/O操作結束之前不被釋放。
對於同步操作來說,這很容易;當然,這個緩沖區在receive和send時都存在。
char buff[512];
   ...
   sock.receive(buffer(buff));
   strcpy(buff, "ok\n");
   sock.send(buffer(buff));
但是在異步操作時就這麼簡單了,看下面的代碼片段:			
// 非常差勁的代碼 ...
   void on_read(const boost::system::error_code & err, std::size_t read_
   bytes)
   { ... }
   void func() {
 char buff[512];
       sock.async_receive(buffer(buff), on_read);
   }
在我們調用async_receive()之後,buff就已經超出有效范圍,它的內存當然會被釋放。當我們開始從套接字接收一些數據時,我們會把它們拷貝到一片已經不屬於我們的內存中;它可能會被釋放,或者被其他代碼重新開辟來存入其他的數據,結果就是:內存沖突。
對於上面的問題有幾個解決方案:
使用全局緩沖區
創建一個緩沖區,然後在操作結束時釋放它
使用一個集合對象管理這些套接字和其他的數據,比如緩沖區數組
第一個方法顯然不是很好,因為我們都知道使用全局變量很不好。此外,如果兩個實例使用同一個緩沖區怎麼辦?
下面是第二種方式的實現:						
    void on_read(char * ptr, const boost::system::error_code & err,
       std::size_t read_bytes) {						
           delete[] ptr;
       }
    
       ....
       char * buff = new char[512];
    
       sock.async_receive(buffer(buff, 512), boost::bind(on_
       read,buff,_1,_2))
    
    或者,如果你想要緩沖區在操作結束後自動超出范圍,使用共享指針
struct shared_buffer {
    boost::shared_array buff;
    int size;
    shared_buffer(size_t size) : buff(new char[size]), size(size) {
    }
    mutable_buffers_1 asio_buff() const {
        return buffer(buff.get(), size);
    }

};

// 當on_read超出范圍時, boost::bind對象被釋放了,

// 同時也會釋放共享指針
void on_read(shared_buffer, const boost::system::error_code & err,
                               std::size_t read_bytes) {}
   sock.async_receive(buff.asio_buff(), boost::bind(on_read,buff,_1,_2));

shared_buffer類擁有實質的shared_array<>,shared_array<>存在的目的是用來保存shared_buffer實例的拷貝-當最後一個share_array<>元素超出范圍時,shared_array<>就被自動銷毀了,而這就是我們想要的結果。

因為Boost.Asio會給完成處理句柄保留一個拷貝,當操作完成時就會調用這個完成處理句柄,所以你的目的達到了。那個拷貝是一個boost::bind的仿函數,它擁有著實際的shared_buffer實例。這是非常優雅的!

第三個選擇是使用一個連接對象來管理套接字和其他數據,比如緩沖區,通常來說這是正確的解決方案但是非常復雜。在這一章的末尾我們會對這種方法進行討論。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved