Qt,设计小型多客户端,多线程,互斥,可收发tcp服务端

该博客介绍了一个使用Qt开发的TCP服务器,具备多线程处理多个客户端连接的能力,实现了互斥机制、高并发处理及接收到的数据按文件夹分类存储。关键代码包括服务端逻辑、子客户端逻辑和主程序。

Tcp服务端,实现了多线程,多客户端,互斥,链表,高并发,接收数据,按文件夹分类写入到文件中,是一个用于调试接收打印log,debug等数据的小型tcp服务器。

总体界面

 界面Widget,关键逻辑代码

TcpSerWidget::TcpSerWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpSerWidget)
{
    //设置相关ui界面
    ui->setupUi(this);
    //初始化界面关联和触发
    Init();
    m_TcpSerFlg=0;
    m_pTcpserver = new MyTcpServer;
    //关联客户端收到的数据,刷新到界面
    connect(m_pTcpserver,&MyTcpServer::SendToWidget,this,&TcpSerWidget::ClientInfoSlots);
}
void TcpSerWidget::Init(){
    //IplineEdit,监听端口
    //只输入整数
    //QRegExp regx("[a-zA-Z0-9]+$");
    QRegExp regx("[0-9]+$");
    QValidator *validator = new QRegExpValidator(regx, ui->IplineEdit );
    ui->IplineEdit->setValidator(validator);
    //默认显示
    ui->IplineEdit->setPlaceholderText("端口");

    //plainTextEdit,发送数据窗口
    //文本改变触发函数
    connect(ui->plainTextEdit,SIGNAL(textChanged()),this,SLOT(plainTextEdit_send_textChanged()));
    ui->plainTextEdit->setPlaceholderText("输入需要发送的数据(最大256字节)");

    //pushButton,监听端口
    //触发函数
    connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(SerListen_pushButton_clicked()));

    //pushButton_2,发送数据按钮
    //触发函数
    connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(pushButton_send_clicked()));

    //ui->textEdit->setFontPointSize(13);
}
//获取本地地址和设置的端口,启动服务器监听
void TcpSerWidget::SerListen_pushButton_clicked(){
    //ui->MesglineEdit->setText(QString(socket->readAll()));
    QString port_qstr=ui->IplineEdit->text();
    quint16 port=port_qstr.toUShort();
    if(port<30000||port>50000){
        port=PORT;
    }
    qDebug("SerListen_pushButton_clicked,port:%s,%d\n",port_qstr.toStdString().c_str(),port);
    if(m_TcpSerFlg!=1){
        //启动日志监听服务

        //监听任何IPv4地址
        m_pTcpserver->listen(QHostAddress::AnyIPv4,port);
        //m_pTcpserver->start();
        //
        //QHostAddress address=QHostAddress("192.168.0.173");
        //m_pTcpserver->listen(address,port);
        m_TcpSerFlg=1;
        char ser_info[128];
        memset(ser_info,0,sizeof(ser_info));
        bool listen_state=m_pTcpserver->isListening();
        QHostAddress SerAddr=m_pTcpserver->serverAddress();
        if(listen_state){
            snprintf(ser_info,sizeof(ser_info),"Listening(%s:%d)...",SerAddr.toString().toStdString().c_str(),port);
        }else{
            snprintf(ser_info,sizeof(ser_info),"Start Listening(%s:%d)->fail",SerAddr.toString().toStdString().c_str(),port);
        }

        qDebug("TcpServer,ser_info:%s\n",ser_info);
        QString state=QString(QLatin1String(ser_info));
        SetSerStatus(state);
    }

}
//发送数据
void TcpSerWidget::pushButton_send_clicked(){
    QString text=ui->plainTextEdit->toPlainText();
    qDebug("触发发送数据:%s",text.toStdString().c_str());
    m_pTcpserver->SendClientData(text);
}

tcp服务端逻辑代码

MyTcpServer::MyTcpServer(QObject * parent) : QTcpServer(parent){
    //启动客户端发送数据监听线程
    m_thr_sendClient = new MyThread(MyTcpServer::runCb,this);
    m_thr_sendClient->start();
}
void MyTcpServer::incomingConnection(qintptr socketDescriptor){
    qDebug() << "TcpServer incomingConnection";
    //QThreadPool::globalInstance()->start(new Mythread(handle));
    QMutexLocker locker(&m_ClientList_mutex);
    //创建tcp客户端类,启动收发
    ClienTcpThread *t = new ClienTcpThread(socketDescriptor);
    t->start();
    connect(t,&ClienTcpThread::SendToSerWidget,this,&MyTcpServer::ClientIpSlots);
    //添加到客户端管理链表
    m_ClientList.push_back(t);
}
void MyTcpServer::ClientIpSlots(QByteArray baIp)
{
   qDebug("MyTcpServer ClientIpInfo:%s\n",baIp.data());
   //发送到界面数据刷新
   emit SendToWidget(baIp);
}
void MyTcpServer::SendClientData(QString data){
    QMutexLocker locker(&m_SendClientData_list_mutex);
    m_SendClientData_list.push_back(data);
}
//每个客户端发送数据线程,有需要发送数据,则送给客户端发送链表中,去等待发送
void MyTcpServer::runCb2(){
    qDebug("hello mytcpserver runCb2");
    qint32 seccnt=0;
    QList<ClienTcpThread *>::const_iterator it_client;
    ClienTcpThread * pcli=Q_NULLPTR;
    QList<QString>::const_iterator it_send;
    QString send_string;
    while (true) {
        QThread::msleep(10);
        //qDebug("mytcpserver run %d",seccnt++);
        if(!m_ClientList.isEmpty()&&!m_SendClientData_list.isEmpty()){
            if(m_SendClientData_list_mutex.tryLock()&&m_ClientList_mutex.tryLock()){
                qDebug("mytcpserver run client:%d,data:%d",m_ClientList.size(),m_SendClientData_list.size());
                it_send = m_SendClientData_list.constBegin();
                for(;it_send!=m_SendClientData_list.constEnd();it_send++){
                    send_string = *it_send;
                    it_client=m_ClientList.constBegin();
                    for(;it_client!=m_ClientList.constEnd();it_client++){
                        pcli=*it_client;
                        pcli->SendData(send_string);
                    }
                    m_SendClientData_list.pop_front();
                }
                m_ClientList_mutex.unlock();
                m_SendClientData_list_mutex.unlock();
            }
        }
    }
}

子客户端逻辑代码

ClienTcpThread::ClienTcpThread(qintptr s)
{
    m_socketDescriptor=s;
    m_bfileopen=false;
}

void ClienTcpThread::run(){
    //char* preadbuf=new char[1024];
    //qint64 readLen=0;
    //创建QTcpSocket
    m_socket = new QTcpSocket();
    if(!m_socket->setSocketDescriptor(m_socketDescriptor)) {
        qDebug() << "tcpSocket setSocketDescriptor(m_socketDescriptor) failed";
        delete m_socket;
        m_socket=NULL;
        return;
    }
    //创建分类文件夹和文件
    QFileInfo info("./QFile.exe");
    QString absolutePath = info.absolutePath();
    QString DbgDirPrx("dvr_");
    QString dirName = absolutePath+"/"+DbgDirPrx+QDateTime::currentDateTime().toString("yyyy_MM_dd");
    QDir dir(dirName);
    if(!dir.exists())
    {
        dir.mkdir(dirName);
        qDebug()<<dirName<<" 文件夹创建成功";
    }
    char path[128]={0};
    snprintf(path,sizeof(path),"%s/%s_%s.log",dirName.toStdString().c_str(),m_socket->peerAddress().toString().toStdString().c_str(),QDateTime::currentDateTime().toString("hhmmss").toStdString().c_str());

    //关联文件名字
    m_file.setFileName(path);
    bool b_open = m_file.open(QIODevice::WriteOnly|QIODevice::Text);
    if(b_open){
        m_bfileopen=true;
    }
     qDebug("open file(%d):%s\n",b_open,path);
    char IPbuf[128]={0};
    snprintf(IPbuf,sizeof(IPbuf),"IP[%s:%d]",m_socket->peerAddress().toString().toStdString().c_str(),m_socket->peerPort());
    QByteArray baIP(IPbuf);
    emit SendToSerWidget(baIP);

    //m_tcpSocket->peerAddress().toString().toStdString().c_str(),m_tcpSocket->peerPort()
    char heartbuf[128]={0};
    //snprintf(heartbuf,sizeof(heartbuf),"heart->%s",IPbuf);
    snprintf(heartbuf,sizeof(heartbuf),"heart");
    qDebug("%s\n",IPbuf);
    //connect(m_socket,&QTcpSocket::readyRead,this,&ClienTcpThread::RcvData);
   // m_socket->waitForReadyRead(1024);
    qint64 curSec = QDateTime::currentDateTime().toTime_t();
    qint64 lastSendSec=curSec;
    qint64 readLen=0;
    const qint64 maxlen=1024*1024;
    char preadbuf[maxlen];
    while(m_socket->state()==QAbstractSocket::ConnectedState)
    {
        curSec = QDateTime::currentDateTime().toTime_t();
        //读数据
        if (m_socket->waitForReadyRead(1000)){
            memset(preadbuf,0,maxlen);
            readLen=m_socket->read(preadbuf,maxlen);  //接收数据
            //cout << stu1.ar << "    " << stu1.b << endl;
            //qDebug("Rcv[%s][%lld]:%s\n",IPbuf,readLen,preadbuf);
            qDebug("Rcv[%s][%lld/%lld]\n",IPbuf,readLen,maxlen);
            if (m_bfileopen)
            {
                qDebug("RcvData,write[%lld]\n",readLen);
                m_file.write(preadbuf,readLen);
                m_file.flush();
            }
        }
        //发数据
        if(abs(curSec-lastSendSec)>5){

            qDebug("[%lld,%s]->heart\n",curSec,IPbuf);
            m_socket->write(heartbuf);
            lastSendSec=curSec;
        }else{
            if(m_SendData_list_mutex.tryLock()){
                if(!m_SendData_list.isEmpty()){
                    qDebug("[%lld,%s]->sendlist:%d\n",curSec,IPbuf,m_SendData_list.size());
                    QList<QString>::const_iterator it=m_SendData_list.constBegin();
                    QString send_string;
                    for(;it!=m_SendData_list.constEnd();it++){
                        send_string=*it;
                        qDebug("[%lld,%s]->send:%s\n",curSec,IPbuf,send_string.toStdString().c_str());
                        m_socket->write(send_string.toLatin1());//按asccii码转QByteArray
                    }
                    m_SendData_list.clear();
                }
                m_SendData_list_mutex.unlock();
            }
        }
        QThread::msleep(100);

    }
    if(m_bfileopen){
        m_file.close();
        m_bfileopen=false;
    }
    //delete preadbuf;
    qDebug("[%s]->end\n",IPbuf);
    if(Q_NULLPTR !=m_socket){
        m_socket->close();
        delete m_socket;
        m_socket=NULL;
    }
}
//添加发送数据到发送链表
qint8 ClienTcpThread::SendData(QString data){
    QMutexLocker locker(&m_SendData_list_mutex);
    m_SendData_list.push_back(data);
    qDebug("send data:%s",data.toStdString().c_str());
    return 0;
}

主mian

    QApplication a(argc, argv);
    //重置qDebug打印函数到文件中
    SetDebugPathFile();
    qInstallMessageHandler(customMessageHandler);

    //MyTcpServer * pTcpServer= new MyTcpServer;
    //监听任何IPv4地址
    //pTcpServer->listen(QHostAddress::AnyIPv4,PORT);

    TcpSerWidget w;
    w.show();

演示,使用程序链接资源下载

https://download.youkuaiyun.com/download/wutu_csdn_blog/87129453

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值