为socket多线程通信添加线程清理功能

为socket多线程通信添加线程清理功能

server_thread_w_clean.cpp

  • server_thread_w_clean.cpp

    #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();
    
    int main(int argc, char *argv[])
    {
        if (server.initServer(6666) == false)
        {
            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)   //键为0是异常情况,若erase键0则会报段错误
                {
                    continue;
                }
                if (server.m_clientaddrs.find((*it).first) == server.m_clientaddrs.end())
                {
                    tcp_map.erase((*it).first);
                }
            }
        }
    }
    
  • 一开始是把通信线程放到vector容器中,但问题是每来一个客户端就往vector里插一个thread,但是客户端断开连接后这个线程还存在vector容器里,这就导致vector容器的size一直在增大

  • 所以不仅要对server.m_clientaddrs这个map容器进行清理,还要对管理通信线程的vector容器进行清理

  • 我尝试了一下午,主要是从线程ID入手,但有一个问题是遍历vector容器时无论这个线程是否正在通信,都会返回non executing thread的thread::id,而且在线程中清理线程还会造成我杀我自己的问题,相当于一个悖论

  • 去健身房健身前突然想到了map容器,既然线程退出时清理了server.m_clientaddrsclientfd,那么如果将clientfd也作为通信thread的键,在新线程开始时遍历这个mapserver.m_clientaddrs里没有的clientfd而这个map有,清除掉即可,这样就可以实现已经挂掉的通信线程的清理

  • 用的时候发现vector容器的emplace_bak可以插入带参数的线程函数,而map容器的emplaceinsert不能插入带参数的线程函数,所以直接用全局变量serverm_connectfd作为tcpFunc()中局部变量clientfd

  • 在遍历tcp_map的时候会出现键为0的情况,这是个异常,因为并没有插入值为0的键,此时如果erase掉就会产生段错误,所以遍历到的话直接continue掉,具体原因还没有深究

  • 将程序部署到云服务器上编译时,会报很多错,搜了一下大概是gcc版本太低的问题。云服务器上式4.8.5而笔记本上是7.8.5,CentOS升级GCC又比较麻烦,所以直接重装系统,GCC版本直接升级到八点多,成功编译


socket线程清理功能修正

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)   //键为0是异常情况,若erase键0则会报段错误
            {
                continue;
            }
            if (server.m_clientaddrs.find((*it).first) == server.m_clientaddrs.end())
            {
                tcp_map.erase((*it).first);
            }
        }
    }
}
  • 当键为0时如果continue还是会报段错误,改为break则没有问题
  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Prejudices

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值