C++中套接字庫(kù)sockpp的使用詳解
sockpp是一個(gè)開源、簡(jiǎn)單、現(xiàn)代的C++套接字庫(kù),地址為: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)套接字句柄,并維持其生命周期。當(dāng)C++對(duì)象超出范圍時(shí),它會(huì)關(guān)閉底層套接字句柄。套接字對(duì)象通常是可移動(dòng)的,但不可復(fù)制??梢允褂?std::move()將套接字從一個(gè)作用域(或線程)傳輸?shù)搅硪粋€(gè)作用域。
2.庫(kù)中的所有代碼都位于sockpp C++命名空間內(nèi)。
3.TCP和其它"流"網(wǎng)絡(luò)應(yīng)用程序通常設(shè)置為服務(wù)器或客戶端。接受器用于創(chuàng)建TCP/流服務(wù)器。它綁定一個(gè)地址并偵聽已知端口以接受傳入連接。 當(dāng)連接被接受時(shí),會(huì)創(chuàng)建一個(gè)新的流式套接字。該新套接字可以直接處理或移動(dòng)到線程(或線程池)進(jìn)行處理。
相反,要?jiǎng)?chuàng)建TCP客戶端,需要?jiǎng)?chuàng)建連接器對(duì)象并將其連接到已知地址(通常是主機(jī)和套接字)的服務(wù)器。連接后,套接字是一種流式套接字,可以直接用于讀寫。
對(duì)于IPv4,sockpp::tcp_acceptor和sockpp::tcp_connector類分別用于創(chuàng)建服務(wù)器和客戶端。它們使用sockpp::inet_address類來指定由32位主機(jī)地址和16位端口號(hào)組成的端點(diǎn)地址。
sockpp::tcp_acceptor通常位于一個(gè)循環(huán)中接受新連接,并將它們傳遞給另一個(gè)進(jìn)程、線程或線程池以與客戶端交互。
TCP客戶端稍微簡(jiǎn)單一些,創(chuàng)建一個(gè)sockpp::tcp_connector對(duì)象并連接,然后可以直接讀寫數(shù)據(jù)。
4.每個(gè)套接字類的默認(rèn)構(gòu)造函數(shù)不執(zhí)行任何操作,只是將底層句柄設(shè)置為INVALID_SOCKET。它們不創(chuàng)建套接字對(duì)象。
5.套接字對(duì)象不是線程安全的。想要有多個(gè)線程從套接字讀取或?qū)懭胩捉幼值膽?yīng)用程序應(yīng)該使用某種形式的序列化,例如std::mutex來保護(hù)訪問。套接字可以安全地從一個(gè)線程移動(dòng)到(moved)另一個(gè)線程。這是服務(wù)器的一種常見模式,它使用一個(gè)線程接受傳入連接,然后將新套接字傳遞給另一個(gè)線程或線程池進(jìn)行處理。
由于套接字無法復(fù)制,唯一的選擇是將套接字移動(dòng)到這樣的函數(shù)。這是一種常見的模式,尤其是在客戶端應(yīng)用程序中,讓一個(gè)線程從套接字讀取數(shù)據(jù),另一個(gè)線程向套接字寫入數(shù)據(jù)。在這種情況下,底層套接字句柄可以被認(rèn)為是線程安全的(一個(gè)讀線程和一個(gè)寫線程)。但即使在這種情況下,sockpp::socket對(duì)象仍然不是線程安全的,特別是由于緩存的錯(cuò)誤值(cached error value)。寫入線程可能會(huì)看到讀取線程上發(fā)生的錯(cuò)誤,反之亦然。
這種情況的解決方案是使用socket::clone()方法來復(fù)制套接字。這將使用系統(tǒng)的dup()函數(shù)或類似的函數(shù)創(chuàng)建另一個(gè)帶有套接字句柄的重復(fù)副本的套接字。這樣做的另一個(gè)好處是套接字的每個(gè)副本都可以保持獨(dú)立的生命周期。在兩個(gè)對(duì)象超出范圍之前,底層套接字不會(huì)關(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的測(cè)試代碼:
1.客戶端測(cè)試代碼如下:
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.服務(wù)器端測(cè)試代碼如下:
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");
}
}
} // namespaceWindows上執(zhí)行結(jié)果如下所示:模擬1個(gè)服務(wù)器端,3個(gè)客戶端

Linux上執(zhí)行結(jié)果如下圖所示:模擬1個(gè)服務(wù)器端,3個(gè)客戶端

GitHub:https://github.com/fengbingchun/OpenSSL_Test
到此這篇關(guān)于C++中套接字庫(kù)sockpp的使用詳解的文章就介紹到這了,更多相關(guān)C++套接字庫(kù)sockpp內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言循環(huán)鏈表實(shí)現(xiàn)貪吃蛇游戲
這篇文章主要為大家詳細(xì)介紹了C語言循環(huán)鏈表實(shí)現(xiàn)貪吃蛇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
用C語言遞歸實(shí)現(xiàn)火車調(diào)度算法詳解
本文主要介紹了用C語言遞歸實(shí)現(xiàn)火車調(diào)度算法詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
利用C語言實(shí)現(xiàn)順序表的實(shí)例操作
順序表是線性表中的一種重要的數(shù)據(jù)結(jié)構(gòu),也是最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),所以他不僅是學(xué)習(xí)中的重點(diǎn),也是應(yīng)用開發(fā)非常常用的一種數(shù)據(jù)結(jié)構(gòu)。這篇文章介紹如何利用C語言實(shí)現(xiàn)順序表。2016-08-08
C++設(shè)置超時(shí)時(shí)間的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了C++設(shè)置超時(shí)時(shí)間的簡(jiǎn)單實(shí)現(xiàn)方法,涉及系統(tǒng)函數(shù)setsockopt對(duì)套接口的操作,具有一定的實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
C++高級(jí)數(shù)據(jù)結(jié)構(gòu)之線段樹
這篇文章主要介紹了C++高級(jí)數(shù)據(jù)結(jié)構(gòu)之線段樹,文章圍繞主題的相關(guān)資料展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
Qt音視頻開發(fā)之利用ffmpeg實(shí)現(xiàn)倍速播放
這篇文章主要為大家詳細(xì)介紹了在Qt音視頻開發(fā)中如何利用ffmpeg實(shí)現(xiàn)倍速播放功能(半倍速/2倍速/4倍速/8倍速),感興趣的小伙伴可以了解一下2022-11-11

