server_thread_w_clean_signal
#include "tcpserver.h"
#include <thread>
#include <mutex>
TcpServer server;
mutex tcp_mutex;
map<int, thread> tcp_map;
void tcpFunc();
void threadExit(int clientfd);
void tcpMapClean();
void mainExit(int sig);
int main(int argc, char *argv[])
{
for (int i = 0; i < 100; i++)
{
signal(i, SIG_IGN);
}
signal(SIGINT, mainExit);
signal(SIGTERM, mainExit);
if (!server.initServer(6666))
{
cout << "服务端初始化失败!!!" << endl;
return -1;
}
cout << "等待客户端连接......" << endl;
while (true)
{
if (!server.tcpAccept())
{
continue;
}
tcp_map.emplace(make_pair(server.m_connectfd, tcpFunc));
if (tcp_map.at(server.m_connectfd).joinable())
{
cout << "Tcp thread " << tcp_map.at(server.m_connectfd).get_id() << " is joinable!" << endl;
tcp_map.at(server.m_connectfd).detach();
}
}
return 0;
}
void tcpFunc()
{
int buf_len = 0;
char buffer[1024];
int clientfd = server.m_connectfd;
cout << "客户端" << server.getClientIP(clientfd) << "已连接" << endl;
cout << "当前客户端数:" << server.m_clientaddrs.size() << endl;
tcpMapClean();
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 << "客户端" << server.getClientIP(clientfd) << "通信异常!" << endl;
threadExit(clientfd);
return;
}
void threadExit(int clientfd)
{
close(clientfd);
server.m_clientaddrs.erase(clientfd);
}
void tcpMapClean()
{
if (tcp_map.empty())
{
cout << "TCP Thread Map is empty!" << endl;
return;
}
else
{
for (auto it = tcp_map.begin(); it != tcp_map.end(); it++)
{
if ((*it).first == 0)
{
break;
}
if (server.m_clientaddrs.find((*it).first) == server.m_clientaddrs.end())
{
tcp_map.erase((*it).first);
}
}
}
}
void mainExit(int sig)
{
cout << "服务端退出......" << endl;
server.closeListen();
server.m_clientaddrs.clear();
tcp_map.clear();
exit(0);
}
- 一开始是把通信线程放到
vector
容器中,但问题是每来一个客户端就往vector
里插一个thread
,但是客户端断开连接后这个线程还存在vector
容器里,这就导致vector
容器的size
一直在增大 - 所以不仅要对
server.m_clientaddrs
这个map
容器进行清理,还要对管理通信线程的vector
容器进行清理 - 我尝试了一下午,主要是从线程ID入手,但有一个问题是遍历vector容器时无论这个线程是否正在通信,都会返回non executing thread的thread::id,而且在线程中清理线程还会造成我杀我自己的问题,相当于一个悖论
- 去健身房健身前突然想到了
map
容器,既然线程退出时清理了server.m_clientaddrs
的clientfd
,那么如果将clientfd
也作为通信thread
的键,在新线程开始时遍历这个map
,server.m_clientaddrs
里没有的clientfd
而这个map
有,清除掉即可,这样就可以实现已经挂掉的通信线程的清理 - 用的时候发现
vector
容器的emplace_bak
可以插入带参数的线程函数,而map
容器的emplace
和insert
不能插入带参数的线程函数,所以直接用全局变量server
的m_connectfd
作为tcpFunc()
中局部变量clientfd
- 在遍历
tcp_map
的时候会出现键为0
的情况,这是个异常,因为并没有插入值为0
的键,此时如果erase
掉就会产生段错误,所以遍历到的话直接continue
掉,具体原因还没有深究 - 将程序部署到云服务器上编译时,会报很多错,搜了一下大概是
gcc版本
太低的问题。云服务器上式4.8.5而笔记本上是7.8.5,CentOS升级GCC
又比较麻烦,所以直接重装系统,GCC
版本直接升级到八点多,成功编译 - 当键为0时如果continue还是会报段错误,改为break则没有问题
- 信号退出很简单,关闭监听的socket,清空两个map容器