C++中套接字庫sockpp的使用詳解
sockpp是一個開源、簡單、現(xiàn)代的C++套接字庫,地址為:https://github.com/fpagliughi/sockpp,最新發(fā)布版本為0.8.1,license為BSD-3-Clause。目前支持Linux、Windows、Mac上的IPv4、IPv6和Unix域套接字。其它*nix和POSIX系統(tǒng)只需很少的修改或無需修改即可工作。
1.套接字基類包裝(wrap)系統(tǒng)套接字句柄,并維持其生命周期。當C++對象超出范圍時,它會關(guān)閉底層套接字句柄。套接字對象通常是可移動的,但不可復制??梢允褂?std::move()將套接字從一個作用域(或線程)傳輸?shù)搅硪粋€作用域。
2.庫中的所有代碼都位于sockpp C++命名空間內(nèi)。
3.TCP和其它"流"網(wǎng)絡(luò)應用程序通常設(shè)置為服務器或客戶端。接受器用于創(chuàng)建TCP/流服務器。它綁定一個地址并偵聽已知端口以接受傳入連接。 當連接被接受時,會創(chuàng)建一個新的流式套接字。該新套接字可以直接處理或移動到線程(或線程池)進行處理。
相反,要創(chuàng)建TCP客戶端,需要創(chuàng)建連接器對象并將其連接到已知地址(通常是主機和套接字)的服務器。連接后,套接字是一種流式套接字,可以直接用于讀寫。
對于IPv4,sockpp::tcp_acceptor和sockpp::tcp_connector類分別用于創(chuàng)建服務器和客戶端。它們使用sockpp::inet_address類來指定由32位主機地址和16位端口號組成的端點地址。
sockpp::tcp_acceptor通常位于一個循環(huán)中接受新連接,并將它們傳遞給另一個進程、線程或線程池以與客戶端交互。
TCP客戶端稍微簡單一些,創(chuàng)建一個sockpp::tcp_connector對象并連接,然后可以直接讀寫數(shù)據(jù)。
4.每個套接字類的默認構(gòu)造函數(shù)不執(zhí)行任何操作,只是將底層句柄設(shè)置為INVALID_SOCKET。它們不創(chuàng)建套接字對象。
5.套接字對象不是線程安全的。想要有多個線程從套接字讀取或?qū)懭胩捉幼值膽贸绦驊撌褂媚撤N形式的序列化,例如std::mutex來保護訪問。套接字可以安全地從一個線程移動到(moved)另一個線程。這是服務器的一種常見模式,它使用一個線程接受傳入連接,然后將新套接字傳遞給另一個線程或線程池進行處理。
由于套接字無法復制,唯一的選擇是將套接字移動到這樣的函數(shù)。這是一種常見的模式,尤其是在客戶端應用程序中,讓一個線程從套接字讀取數(shù)據(jù),另一個線程向套接字寫入數(shù)據(jù)。在這種情況下,底層套接字句柄可以被認為是線程安全的(一個讀線程和一個寫線程)。但即使在這種情況下,sockpp::socket對象仍然不是線程安全的,特別是由于緩存的錯誤值(cached error value)。寫入線程可能會看到讀取線程上發(fā)生的錯誤,反之亦然。
這種情況的解決方案是使用socket::clone()方法來復制套接字。這將使用系統(tǒng)的dup()函數(shù)或類似的函數(shù)創(chuàng)建另一個帶有套接字句柄的重復副本的套接字。這樣做的另一個好處是套接字的每個副本都可以保持獨立的生命周期。在兩個對象超出范圍之前,底層套接字不會關(guān)閉。
在Windows和Linux上編譯的sockpp的shell腳本如下:
#! /bin/bash if [ $# != 2 ]; then echo "Error: requires two parameters: 1: windows or linux; 2: release or debug" echo "For example: $0 windows debug" exit -1 fi if [ $1 != "windows" ] && [ $1 != "linux" ]; then echo "Error: the first parameter can only be windows or linux" exit -1 fi if [ $2 != "debug" ] && [ $2 != "release" ]; then echo "Error: the second parameter can only be debug or release" exit -1 fi if [ $1 == "windows" ] && [ $2 == "debug" ]; then cmake \ -G"Visual Studio 17 2022" -A x64 \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CONFIGURATION_TYPES=Debug \ -DSOCKPP_BUILD_SHARED=OFF \ -DSOCKPP_BUILD_STATIC=ON \ -DSOCKPP_BUILD_EXAMPLES=ON \ -DCMAKE_INSTALL_PREFIX=install/debug \ -Bbuild \ . cmake --build build/ --target install --config debug fi if [ $1 == "windows" ] && [ $2 == "release" ]; then cmake \ -G"Visual Studio 17 2022" -A x64 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CONFIGURATION_TYPES=Release \ -DSOCKPP_BUILD_SHARED=OFF \ -DSOCKPP_BUILD_STATIC=ON \ -DSOCKPP_BUILD_EXAMPLES=ON \ -DCMAKE_INSTALL_PREFIX=install/release \ -Bbuild \ . cmake --build build/ --target install --config release fi if [ $1 == "linux" ] && [ $2 == "debug" ]; then cmake \ -DCMAKE_BUILD_TYPE=Debug \ -DSOCKPP_BUILD_SHARED=OFF \ -DSOCKPP_BUILD_STATIC=ON \ -DSOCKPP_BUILD_EXAMPLES=ON \ -DCMAKE_INSTALL_PREFIX=install/debug \ -Bbuild \ . cmake --build build/ --target install --config debug fi if [ $1 == "linux" ] && [ $2 == "release" ]; then cmake \ -DCMAKE_BUILD_TYPE=Release \ -DSOCKPP_BUILD_SHARED=OFF \ -DSOCKPP_BUILD_STATIC=ON \ -DSOCKPP_BUILD_EXAMPLES=ON \ -DCMAKE_INSTALL_PREFIX=install/release \ -Bbuild \ . cmake --build build/ --target install --config release fi rc=$? if [[ ${rc} != 0 ]]; then echo "Error: please check: ${rc}" exit ${rc} fi
以下為IPv4的測試代碼:
1.客戶端測試代碼如下:
int test_sockpp_client() { sockpp::initialize(); sockpp::tcp_connector conn({host, port}); if (!conn) { std::cerr << "Error: connecting to server at: " << sockpp::inet_address(host, port) << ", message: " << conn.last_error_str() << std::endl; return -1; } std::cout << "created a connection from: " << conn.address() << std::endl; std::cout << "created a connection to " << conn.peer_address() << std::endl; // set a timeout for the responses if (!conn.read_timeout(std::chrono::seconds(5))) { std::cerr << "Error: setting timeout on TCP stream: " << conn.last_error_str() << std::endl; } const std::vector<std::string> addr{"csdn", "github", "gitlab"}; std::unique_ptr<unsigned char[]> buf(new unsigned char[len]); int index{0}; std::atomic<bool> quit{ false }; std::thread th([&quit] { std::this_thread::sleep_for(std::chrono::seconds(20)); quit = true; }); while (true) { if (quit) break; auto ret = conn.write(addr[index]); if (ret != addr[index].size()) { std::cerr << "Error: writing to the TCP stream: " << conn.last_error_str() << std::endl; break; } memset(buf.get(), 0, len); ret = conn.read(buf.get(), len); if (ret == -1) { std::cerr << "Error: reading from TCP stream: " << conn.last_error_str() << std::endl; break; } std::cout << addr[index] << ": " << buf.get() << std::endl; if (++index == addr.size()) index = 0; std::this_thread::sleep_for(std::chrono::seconds(1)); } th.join(); return 0; }
2.服務器端測試代碼如下:
int test_sockpp_server() { sockpp::initialize(); sockpp::tcp_acceptor acc(port); if (!acc) { std::cerr << "Error: creating the acceptor: " << acc.last_error_str() << std::endl; return -1; } while (true) { sockpp::inet_address peer; // accept a new client connection sockpp::tcp_socket sock = acc.accept(&peer); std::cout << "received a connection request from: " << peer << std::endl; if (!sock) { std::cerr << "Error: accepting incoming connection: " << acc.last_error_str() << std::endl; } else { // create a thread and transfer the new stream to it std::thread th2(run_echo, std::move(sock)); th2.detach(); } } return 0; }
3.輔助code如下所示:
namespace { constexpr char* host{"127.0.0.1"}; constexpr in_port_t port{ 8888 }; constexpr int len {64}; void run_echo(sockpp::tcp_socket sock) { std::cout << "thread id: " << std::this_thread::get_id() << std::endl; std::map<std::string, std::string> addr; addr["csdn"] = "https://blog.csdn.net/fengbingchun"; addr["github"] = "https://github.com/fengbingchun"; std::unique_ptr<unsigned char[]> buf(new unsigned char[len]); while (true) { memset(buf.get(), 0, len); auto ret = sock.read(buf.get(), len); if (ret == -1) { std::cerr << "Error: reading from TCP stream: " << sock.last_error_str() << std::endl; break; } auto it = addr.find(std::string((char*)buf.get())); if (it != addr.end()) { sock.write(it->second); } else sock.write("unkonwn"); } } } // namespace
Windows上執(zhí)行結(jié)果如下所示:模擬1個服務器端,3個客戶端
Linux上執(zhí)行結(jié)果如下圖所示:模擬1個服務器端,3個客戶端
GitHub:https://github.com/fengbingchun/OpenSSL_Test
到此這篇關(guān)于C++中套接字庫sockpp的使用詳解的文章就介紹到這了,更多相關(guān)C++套接字庫sockpp內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++高級數(shù)據(jù)結(jié)構(gòu)之線段樹
這篇文章主要介紹了C++高級數(shù)據(jù)結(jié)構(gòu)之線段樹,文章圍繞主題的相關(guān)資料展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-05-05Qt音視頻開發(fā)之利用ffmpeg實現(xiàn)倍速播放
這篇文章主要為大家詳細介紹了在Qt音視頻開發(fā)中如何利用ffmpeg實現(xiàn)倍速播放功能(半倍速/2倍速/4倍速/8倍速),感興趣的小伙伴可以了解一下2022-11-11