C++ Thread實現簡單的socket多線程通信
起因
為什麼要用C++的Thread,很簡單,因為我菜
一打五用pthread實現瞭socket多線程通信,我之前學並發的時候沒看pthread,因此代碼隻能看個大概,後面還是要系統學一下pthread的
服務端
多線程功能放在騰訊雲服務器上,代碼如下:
#include "tcpserver.h" #include <thread> #include <mutex> TcpServer server; mutex tcp_mutex; void tcpFunc(int clientfd); int main(int argc, char *argv[]) { if (server.initServer(6666) == false) { cout << "服務端初始化失敗!!!" << endl; return -1; } vector<thread> tcp_vec; while (true) { if (!server.tcpAccept()) { continue; } tcp_vec.emplace_back(tcpFunc, server.m_connectfd); // thread tcpThread(tcpFunc, server.m_connectfd); // if (tcpThread.joinable()) if(tcp_vec.back().joinable()) { // cout << "Tcp thread " << tcpThread.get_id() << "is joinable!" << endl; cout << "Tcp thread " << tcp_vec.back().get_id() << " is joinable!" << endl; tcp_vec.back().detach(); } } return 0; } void tcpFunc(int clientfd) { int buf_len = 0; char buffer[1024]; while (true) { unique_lock<mutex> tcplck(tcp_mutex); memset(buffer, 0, sizeof(buffer)); if (!server.tcpRecv(clientfd, buffer, &buf_len, 5)) { cout << "接收客戶端數據失敗!" << endl; tcplck.unlock(); break; } cout << "服務端接收數據:" << buffer << endl; strcpy(buffer, "I am your father!"); if (!server.tcpSend(clientfd, buffer, sizeof(buffer))) { cout << "向客戶端發送數據失敗!" << endl; tcplck.unlock(); break; } tcplck.unlock(); usleep(100); } cout << "通信異常!" << endl; return; }
實在是很簡單,貽笑大方瞭
有幾個註意點:
- 全局變量在main函數執行完後會銷毀,線程中用到瞭全局變量server,線程detach後要保證數據的收發,就要保持server的生存期,這裡體現為在main中循環等待客戶端的連接
- 要用鎖鎖住線程中server的操作,避免不同線程同時操作server造成混亂
- usleep(100);是為瞭避免不同線程爭搶同一把鎖而造成死鎖的發生
ROS客戶端
#include "tcpclient.h" #include <ros/ros.h> #include <geometry_msgs/Twist.h> TcpClient client; string send_str = "I am king of the world!"; char recv_buff[1024]; void client_callback(const geometry_msgs::Twist::ConstPtr &msg) { cout << "vel X:" << msg->linear.x << ";vel Y:" << msg->linear.y << ";angular Z:" << msg->angular.z << endl; if (!client.tcpSend(client.m_sockfd, send_str.data(), send_str.size())) { cout << "向服務端發送報文失敗!" << endl; } if (!client.tcpRecv(client.m_sockfd, recv_buff, NULL, 10)) { cout << "從服務端接收報文失敗!" << endl; } cout << "接收服務端報文:" << recv_buff << endl << endl; } int main(int argc, char **argv) { ros::init(argc, argv, "joystick_client"); ros::NodeHandle nh; string server_ip = "1.116.137.21"; string loop_ip = "127.0.0.1"; if (client.connectToServer(server_ip.data(), 6666) == false) { cout << "連接失敗!!!" << endl; return -1; } ros::Subscriber sub = nh.subscribe("/cmd_vel", 1, client_callback); ros::spin(); }
很簡單,訂閱瞭手柄發佈的話題/cmd_vel,在回調函數中和服務端通訊
話題的發佈頻率是10Hz,意味著和服務端通訊的頻率也是10Hz
普通客戶端
#include "tcp/tcpclient.h" int main(int argc, char **argv) { TcpClient client; string server_ip = "1.116.137.21"; string loop_ip = "127.0.0.1"; if (client.connectToServer(server_ip.data(), 6666) == false) { cout << "連接失敗!!!" << endl; return -1; } cout << "成功連接服務器!" << endl; char buff[1024]; while (true) { memset(buff, 0, sizeof(buff)); sprintf(buff, "Ouch!"); if (!client.tcpSend(client.m_sockfd, buff, sizeof(buff))) { cout << "向服務端發送報文失敗!" << endl; return -1; } memset(buff, 0, sizeof(buff)); if (!client.tcpRecv(client.m_sockfd, buff, NULL, 5)) { cout << "從服務端接收報文失敗!" << endl; return -1; } cout << "接收服務端報文:" << buff << endl << endl; sleep(0.1); } return 0; }
這裡sleep(0.1);是為瞭模擬ROS中話題的頻率
sleep過長會導致服務端阻塞等待該客戶端的消息,從而導致其餘客戶端與服務端的通信失敗(如果客戶端中允許的通信延時很短的話)
運行效果
雲服務器上的服務端
[root@VM-4-11-centos bin]# ./server_thread
Tcp thread 140662362572544 is joinable!
服務端接收數據:I am king of the world!
服務端接收數據:I am king of the world!
服務端接收數據:I am king of the world!
服務端接收數據:I am king of the world!
Tcp thread 140662354179840 is joinable!
服務端接收數據:I am king of the world!
服務端接收數據:Ouch!
服務端接收數據:I am king of the world!
服務端接收數據:Ouch!
服務端接收數據:I am king of the world!
服務端接收數據:Ouch!
服務端接收數據:I am king of the world!
服務端接收數據:Ouch!
筆記本上的ROS客戶端
redwall@redwall-G3-3500:~$ rosrun joystick_client joystick_client
[ERROR] [1656939307.244367879]: [registerPublisher] Failed to contact master at [localhost:11311]. Retrying…
[ INFO] [1656939314.923909682]: Connected to master at [localhost:11311]
vel X:0;vel Y:0;angular Z:0
接收服務端報文:I am your father!vel X:0;vel Y:0;angular Z:0
接收服務端報文:I am your father!vel X:0;vel Y:0;angular Z:0
接收服務端報文:I am your father!
虛擬機的普通客戶端
- prejudice@prejudice-VirtualBox:~/socket_test/socket_for_linux/bin$ ./tcp_client 成功連接服務器!
- 接收服務端報文:I am your father!
- 接收服務端報文:I am your father!
- 接收服務端報文:I am your father!
不足
- 未考慮線程的清理
- 未考慮信號的退出處理
到此這篇關於C++ Thread實現簡單的socket多線程通信的文章就介紹到這瞭,更多相關C++ socket多線程通信內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- C++實現Window環境聊天室功能
- C++詳解如何實現兩個線程交替打印
- 聊聊c++數組名稱和sizeof的問題
- C++基於socket UDP網絡編程實現簡單聊天室功能
- C++基於socket多線程實現網絡聊天室