C++ Thread實(shí)現(xiàn)簡(jiǎn)單的socket多線程通信
起因
為什么要用C++的Thread,很簡(jiǎn)單,因?yàn)槲也?/p>
一打五用pthread實(shí)現(xiàn)了socket多線程通信,我之前學(xué)并發(fā)的時(shí)候沒(méi)看pthread,因此代碼只能看個(gè)大概,后面還是要系統(tǒng)學(xué)一下pthread的
服務(wù)端
多線程功能放在騰訊云服務(wù)器上,代碼如下:
#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 << "服務(wù)端初始化失?。?!!" << 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 << "接收客戶(hù)端數(shù)據(jù)失敗!" << endl; ? ? ? ? ? ? tcplck.unlock(); ? ? ? ? ? ? break; ? ? ? ? } ? ? ? ? cout << "服務(wù)端接收數(shù)據(jù):" << buffer << endl; ? ? ? ? strcpy(buffer, "I am your father!"); ? ? ? ? if (!server.tcpSend(clientfd, buffer, sizeof(buffer))) ? ? ? ? { ? ? ? ? ? ? cout << "向客戶(hù)端發(fā)送數(shù)據(jù)失敗!" << endl; ? ? ? ? ? ? tcplck.unlock(); ? ? ? ? ? ? break; ? ? ? ? } ? ? ? ? tcplck.unlock(); ?? ??? ??? ??? ?usleep(100); ? ? } ? ? cout << "通信異常!" << endl; ? ? return; }
實(shí)在是很簡(jiǎn)單,貽笑大方了
有幾個(gè)注意點(diǎn):
- 全局變量在main函數(shù)執(zhí)行完后會(huì)銷(xiāo)毀,線程中用到了全局變量server,線程detach后要保證數(shù)據(jù)的收發(fā),就要保持server的生存期,這里體現(xiàn)為在main中循環(huán)等待客戶(hù)端的連接
- 要用鎖鎖住線程中server的操作,避免不同線程同時(shí)操作server造成混亂
- usleep(100);是為了避免不同線程爭(zhēng)搶同一把鎖而造成死鎖的發(fā)生
ROS客戶(hù)端
#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 << "向服務(wù)端發(fā)送報(bào)文失敗!" << endl; ? ? } ? ? if (!client.tcpRecv(client.m_sockfd, recv_buff, NULL, 10)) ? ? { ? ? ? ? cout << "從服務(wù)端接收?qǐng)?bào)文失?。? << endl; ? ? } ? ? cout << "接收服務(wù)端報(bào)文:" << 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(); }
很簡(jiǎn)單,訂閱了手柄發(fā)布的話(huà)題/cmd_vel,在回調(diào)函數(shù)中和服務(wù)端通訊
話(huà)題的發(fā)布頻率是10Hz,意味著和服務(wù)端通訊的頻率也是10Hz
普通客戶(hù)端
#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 << "成功連接服務(wù)器!" << endl; ? ? char buff[1024]; ? ? while (true) ? ? { ? ? ? ? memset(buff, 0, sizeof(buff)); ? ? ? ? sprintf(buff, "Ouch!"); ? ? ? ? if (!client.tcpSend(client.m_sockfd, buff, sizeof(buff))) ? ? ? ? { ? ? ? ? ? ? cout << "向服務(wù)端發(fā)送報(bào)文失敗!" << endl; ? ? ? ? ? ? return -1; ? ? ? ? } ? ? ? ? memset(buff, 0, sizeof(buff)); ? ? ? ? if (!client.tcpRecv(client.m_sockfd, buff, NULL, 5)) ? ? ? ? { ? ? ? ? ? ? cout << "從服務(wù)端接收?qǐng)?bào)文失敗!" << endl; ? ? ? ? ? ? return -1; ? ? ? ? } ? ? ? ? cout << "接收服務(wù)端報(bào)文:" << buff << endl << endl; ? ? ? ? sleep(0.1); ? ? } ? ? return 0; }
這里sleep(0.1);是為了模擬ROS中話(huà)題的頻率
sleep過(guò)長(zhǎng)會(huì)導(dǎo)致服務(wù)端阻塞等待該客戶(hù)端的消息,從而導(dǎo)致其余客戶(hù)端與服務(wù)端的通信失敗(如果客戶(hù)端中允許的通信延時(shí)很短的話(huà))
運(yùn)行效果
云服務(wù)器上的服務(wù)端
[root@VM-4-11-centos bin]# ./server_thread
Tcp thread 140662362572544 is joinable!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):I am king of the world!
Tcp thread 140662354179840 is joinable!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):Ouch!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):Ouch!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):Ouch!
服務(wù)端接收數(shù)據(jù):I am king of the world!
服務(wù)端接收數(shù)據(jù):Ouch!
筆記本上的ROS客戶(hù)端
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
接收服務(wù)端報(bào)文:I am your father!vel X:0;vel Y:0;angular Z:0
接收服務(wù)端報(bào)文:I am your father!vel X:0;vel Y:0;angular Z:0
接收服務(wù)端報(bào)文:I am your father!
虛擬機(jī)的普通客戶(hù)端
- prejudice@prejudice-VirtualBox:~/socket_test/socket_for_linux/bin$ ./tcp_client 成功連接服務(wù)器!
- 接收服務(wù)端報(bào)文:I am your father!
- 接收服務(wù)端報(bào)文:I am your father!
- 接收服務(wù)端報(bào)文:I am your father!
不足
- 未考慮線程的清理
- 未考慮信號(hào)的退出處理
到此這篇關(guān)于C++ Thread實(shí)現(xiàn)簡(jiǎn)單的socket多線程通信的文章就介紹到這了,更多相關(guān)C++ socket多線程通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ 封裝 DLL 供 C# 調(diào)用詳細(xì)介紹
這篇文章主要介紹了C++ 封裝 DLL 供 C# 調(diào)用(以C# 調(diào)用C++ 二次封裝的VLC播放庫(kù)為介質(zhì),支持回調(diào)函數(shù)的封裝),需要的朋友可以參考下面我文章的具體內(nèi)容2021-09-09C語(yǔ)言文件操作 fopen, fclose, mkdir詳解
本文給大家詳細(xì)介紹了下C語(yǔ)言的文件操作函數(shù)fopen, fclose, mkdir的用法及示例,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。2016-03-03C++對(duì)cin輸入字符的判斷及分段函數(shù)處理方法示例
這篇文章主要介紹了C++對(duì)cin輸入字符的判斷及分段函數(shù)處理方法,結(jié)合實(shí)例形式分析了C++輸入判斷及處理相關(guān)操作技巧,需要的朋友可以參考下2017-09-09C++中二進(jìn)制數(shù)據(jù)序列化和反序列化詳解
這篇文章主要為大家詳細(xì)介紹了C++中二進(jìn)制數(shù)據(jù)序列化和反序列化的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10