C++?socket通信遇到的問題及解決方法
為了解決這兩個問題,你可以對代碼進行以下修改:
1.處理socket斷開的情況:
在recv函數(shù)返回0的判斷分支中,你可以在斷開連接時進行一些處理,例如打印提示信息或進行重連操作。
if (ret == 0) { printf("Remote socket close!!!\n"); // 處理socket斷開,例如進行重連操作 Reconnect(); break; }
你可以在Reconnect函數(shù)中實現(xiàn)重新連接的邏輯。
2.處理socket連接沒有發(fā)送消息的情況:
在接收數(shù)據(jù)失敗的判斷分支中,你可以添加一個超時機制,如果在一定時間內(nèi)沒有接收到數(shù)據(jù),就認為連接沒有發(fā)送消息。
if (ret < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { std::this_thread::sleep_for(std::chrono::microseconds(10)); continue; } if (errno == EWOULDBLOCK || errno == EAGAIN) { // 沒有數(shù)據(jù)可讀,可以繼續(xù)執(zhí)行其他任務 printf("Failed to receive data!!!\n"); // 處理連接沒有發(fā)送消息的情況,例如重新發(fā)送消息或斷開連接 HandleNoMessage(); continue; } else { printf("Remote socket error!!!\n"); break; } }
你可以在HandleNoMessage函數(shù)中實現(xiàn)處理連接沒有發(fā)送消息的邏輯。
請注意,以上只是一種修改代碼的思路,具體的實現(xiàn)方式可能需要根據(jù)你的需求和業(yè)務邏輯來進行調(diào)整。
要將這段C++代碼改為非阻塞執(zhí)行,需要使用非阻塞的套接字操作和事件循環(huán)機制。下面是修改后的代碼:
void NtripServer::ThreadHandler(void) { int ret; char recv_buffer[1024] = {}; thread_is_running_ = true; // 將socket_fd_設置為非阻塞模式 if (SetNonBlocking(socket_fd_) == -1) { printf("Failed to set socket to non-blocking mode\n"); return; } while (thread_is_running_) { ret = recv(socket_fd_, recv_buffer, sizeof(recv_buffer), 0); if (ret == 0) { printf("Remote socket close!!!\n"); break; } else if (ret < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { printf("Remote socket error!!!\n"); break; } } else { // 處理接收到的數(shù)據(jù) } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } close(socket_fd_); socket_fd_ = -1; thread_is_running_ = false; service_is_running_ = false; }
在修改后的代碼中,我們首先調(diào)用SetNonBlocking函數(shù)將socket_fd_設置為非阻塞模式。然后,在while循環(huán)中,我們使用recv函數(shù)接收數(shù)據(jù),并根據(jù)返回值進行處理。
如果recv返回值為0,表示遠程socket關閉,我們打印相應的信息并跳出循環(huán)。
如果recv返回值小于0,我們首先判斷errno是否為EAGAIN、EWOULDBLOCK或EINTR。如果不是這些錯誤碼,表示發(fā)生了其他錯誤,我們打印相應的信息并跳出循環(huán)。
如果recv返回值大于0,表示成功接收到數(shù)據(jù),我們可以在代碼中添加處理接收到的數(shù)據(jù)的邏輯。
在每次循環(huán)結束后,我們使用std::this_thread::sleep_for函數(shù)暫停10毫秒,以避免忙等待。
最后,在循環(huán)結束后,我們關閉socket_fd_并將其置為-1,同時將thread_is_running_和service_is_running_標志置為false,表示線程已停止。
是的,SetNonBlocking函數(shù)需要自定義。下面是一個示例的SetNonBlocking函數(shù)的實現(xiàn):
int SetNonBlocking(int socket_fd) { int flags = fcntl(socket_fd, F_GETFL, 0); if (flags == -1) { return -1; } if (fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { return -1; } return 0; }
這個函數(shù)的實現(xiàn)與之前提到的設置socket為非阻塞模式的代碼相同。它首先使用fcntl函數(shù)獲取socket_fd的文件描述符標志,并將返回值保存在變量flags中。如果獲取失敗,函數(shù)返回-1。
然后,它使用fcntl函數(shù)的F_SETFL命令來修改socket_fd的文件描述符標志。修改的方式是將O_NONBLOCK標志添加到原有標志中,使用位運算符|進行操作。如果修改失敗,函數(shù)返回-1。
如果設置成功,函數(shù)返回0。
下面是修改后的完整代碼示例:
#include <iostream> #include <thread> #include <chrono> #include <cstring> #include <unistd.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> class NtripServer { public: NtripServer() : socket_fd_(-1), thread_is_running_(false), service_is_running_(false) {} ~NtripServer() {} void Start() { if (service_is_running_) { std::cout << "Service is already running!" << std::endl; return; } // 創(chuàng)建socket socket_fd_ = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd_ == -1) { std::cout << "Failed to create socket!" << std::endl; return; } // 設置socket選項 int optval = 1; setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); // 綁定地址和端口 sockaddr_in server_addr{}; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(1234); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(socket_fd_, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { std::cout << "Failed to bind address!" << std::endl; close(socket_fd_); socket_fd_ = -1; return; } // 監(jiān)聽連接 if (listen(socket_fd_, 5) == -1) { std::cout << "Failed to listen!" << std::endl; close(socket_fd_); socket_fd_ = -1; return; } // 啟動線程處理連接 service_is_running_ = true; std::thread thread(&NtripServer::ThreadHandler, this); thread.detach(); } void Stop() { if (!service_is_running_) { std::cout << "Service is not running!" << std::endl; return; } service_is_running_ = false; } private: int socket_fd_; bool thread_is_running_; bool service_is_running_; void ThreadHandler() { int ret; char recv_buffer[1024] = {}; thread_is_running_ = true; // 將socket_fd_設置為非阻塞模式 if (SetNonBlocking(socket_fd_) == -1) { std::cout << "Failed to set socket to non-blocking mode" << std::endl; return; } while (thread_is_running_) { ret = recv(socket_fd_, recv_buffer, sizeof(recv_buffer), 0); if (ret == 0) { std::cout << "Remote socket close!!!" << std::endl; break; } else if (ret < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { std::cout << "Remote socket error!!!" << std::endl; break; } } else { // 處理接收到的數(shù)據(jù) std::cout << "Received data: " << recv_buffer << std::endl; } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } close(socket_fd_); socket_fd_ = -1; thread_is_running_ = false; service_is_running_ = false; } int SetNonBlocking(int socket_fd) { int flags = fcntl(socket_fd, F_GETFL, 0); if (flags == -1) { return -1; } if (fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) == -1) { return -1; } return 0; } }; int main() { NtripServer server; server.Start(); // 服務運行一段時間后停止 std::this_thread::sleep_for(std::chrono::seconds(10)); server.Stop(); return 0; }
在這個示例代碼中,我們創(chuàng)建了一個NtripServer類來管理Ntrip服務。在Start函數(shù)中,我們創(chuàng)建了一個socket,并設置選項、綁定地址和端口,然后開始監(jiān)聽連接。接著,我們啟動一個線程來處理連接,線程的入口函數(shù)是ThreadHandler。
在ThreadHandler函數(shù)中,我們將socket_fd_設置為非阻塞模式,并使用recv函數(shù)接收數(shù)據(jù)。根據(jù)返回值進行處理,如果返回值為0,表示遠程socket關閉;如果返回值小于0,并且errno不是EAGAIN、EWOULDBLOCK或EINTR,表示發(fā)生了其他錯誤;如果返回值大于0,表示成功接收到數(shù)據(jù),我們可以在代碼中添加處理接收到的數(shù)據(jù)的邏輯。
在主函數(shù)中,我們創(chuàng)建了一個NtripServer對象,并調(diào)用Start函數(shù)啟動服務。然后,讓服務運行一段時間后調(diào)用Stop函數(shù)停止服務。
請注意,這只是一個簡單的示例代碼,實際應用中可能需要進行更多的錯誤處理和邏輯處理。對于Linux系統(tǒng),可以使用fcntl函數(shù)來設置socket為非阻塞模式。在上述代碼中,SetNonBlocking函數(shù)就是用來設置socket為非阻塞的。具體來說,它通過調(diào)用fcntl函數(shù),使用F_GETFL命令獲取socket的當前標志位,然后將O_NONBLOCK標志位加入到標志位中,再次調(diào)用fcntl函數(shù),使用F_SETFL命令將新的標志位設置回socket。
這樣設置之后,socket的recv函數(shù)將變?yōu)榉亲枞J?,即不會一直等待?shù)據(jù)到達,而是立即返回。如果沒有數(shù)據(jù)到達,recv函數(shù)將返回-1,并且errno會被設置為EAGAIN或EWOULDBLOCK。這樣,我們可以在代碼中根據(jù)返回值和errno來判斷是否有數(shù)據(jù)到達,并進行相應的處理。如果在SetNonBlocking函數(shù)中設置socket為非阻塞模式失敗,可以在調(diào)用該函數(shù)的地方進行錯誤處理。可以根據(jù)具體情況選擇合適的錯誤處理方式,例如打印錯誤信息、記錄日志、關閉socket等。以下是一個示例的錯誤處理代碼:
if (SetNonBlocking(socket_fd) == -1) { std::cout << "Failed to set socket to non-blocking mode" << std::endl; // 處理錯誤,例如關閉socket close(socket_fd); return; }
在上述代碼中,如果SetNonBlocking函數(shù)返回-1,表示設置socket為非阻塞模式失敗。我們可以打印錯誤信息,然后進行相應的錯誤處理,例如關閉socket。這樣可以確保在設置失敗的情況下,不會繼續(xù)使用這個socket進行后續(xù)操作。
相關文章
C語言使用openSSL庫DES模塊實現(xiàn)加密功能詳解
這篇文章主要介紹了C語言使用openSSL庫DES模塊實現(xiàn)加密功能,簡單講解了DES加密的相關概念,并結合實例形式分析了DES加密的具體實現(xiàn)技巧,需要的朋友可以參考下2017-05-05C++11中的智能指針shared_ptr、weak_ptr源碼解析
本文是基于gcc-4.9.0的源代碼進行分析,shared_ptr和weak_ptr是C++11才加入標準的,僅對C++智能指針shared_ptr、weak_ptr源碼進行解析,需要讀者有一定的C++基礎并且對智能指針有所了解2021-09-09