seattle^-^ 发表于 2019-12-10 21:28

QT开发TCP协议的通信软件

本帖最后由 seattle^-^ 于 2019-12-10 21:52 编辑

大二课设课程结束了,来总结一下这次的实战QT开发。虽然代码不精致,但是也是笔者一个月的成果。喜欢的来看啊:lol
先上两张本地测试的图片
首先是客户端

然后是服务端




然后接下来是效果图,服务端跟客户端放在一张图里了,图标取自网络,如有侵权,联系笔者。




然后接下来是测试图,时间紧迫,客户端只设置了一个账号,输入账号密码之后进入到此界面







好了,这些是我们小组的项目。写起来也很简单啊,采用QT开发C++语言。


QT开发的这种软件还是相对比较简单的啊。记录一下代码:客户端采用了QT提供的信号与槽,对各种点击事件发出信号,代码段做出反应。客户端的对话框代码段对信息文本的处理,来判断服务器转发的信息与自己的信息来做出不同的文本显示:void qtchatmessage::paintEvent(QPaintEvent *)    //画家
{
    QPainter painter(this);    //画家
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);//消锯齿
    painter.setPen(Qt::NoPen);   //画笔
    painter.setBrush(QBrush(Qt::gray));//画刷


    if(m_userType == User_Type::User_He) { // 用户
      //头像
      painter.drawPixmap(m_iconLeftRect, m_leftPixmap);


      //框加边
      QColor col_KuangB(234, 234, 234);
      painter.setBrush(QBrush(col_KuangB));
      painter.drawRoundedRect(m_kuangLeftRect.x()-1,m_kuangLeftRect.y()-1,m_kuangLeftRect.width()+2,m_kuangLeftRect.height()+2,4,4);
      //框
      QColor col_Kuang(255,255,255);
      painter.setBrush(QBrush(col_Kuang));
      painter.drawRoundedRect(m_kuangLeftRect,4,4);


      //三角
      QPointF points = {
            QPointF(m_sanjiaoLeftRect.x(), 30),
            QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 25),
            QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 35),
      };
      QPen pen;
      pen.setColor(col_Kuang);
      painter.setPen(pen);
      painter.drawPolygon(points, 3);


      //三角加边
      QPen penSanJiaoBian;
      penSanJiaoBian.setColor(col_KuangB);
      painter.setPen(penSanJiaoBian);
      painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 24));
      painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 36));


      //内容
      QPen penText;
      penText.setColor(QColor(51,51,51));
      painter.setPen(penText);
      QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
      option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
      painter.setFont(this->font());
      painter.drawText(m_textLeftRect, m_msg,option);
    }else if(m_userType == User_Type::User_Me) { // 自己
      //头像
      painter.drawPixmap(m_iconRightRect, m_rightPixmap);             //画头像加头像的框架。框加图


      //框
      QColor col_Kuang(75,164,242);      //颜料
      painter.setBrush(QBrush(col_Kuang));    //设置画刷,参数为颜色
      painter.drawRoundedRect(m_kuangRightRect,4,4);    //画给定矩形框的圆角


      //三角
      QPointF points = {
            QPointF(m_sanjiaoRightRect.x()+m_sanjiaoRightRect.width(), 30),   //给定的坐标构造点
            QPointF(m_sanjiaoRightRect.x(), 25),
            QPointF(m_sanjiaoRightRect.x(), 35),
      };
      QPen pen;             //画笔
      pen.setColor(col_Kuang);
      painter.setPen(pen);   //把pen给painter
      painter.drawPolygon(points, 3);//绘制数组中的这三个点


      //内容
      QPen penText;   //创建画笔
      penText.setColor(Qt::white);
      painter.setPen(penText);    //笔给画家
      QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);    //选项    左和水平居中
      option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);   //设置换行模式
      painter.setFont(this->font());   //为画家选择字体
      painter.drawText(m_textRightRect,m_msg,option);   //往矩形框中按照option画入m_msg文本
    }
}





接下来是客户端的送槽函数,用来获取输入的文本,对文本向绘图事件虚函数传递参数,然后向对话框显示文本。
void aWidget::on_pushButton_clicked()    //发送按钮的槽函数
{
    QString str = ui->textEdit_2->toPlainText();   //获取发送区的内容
    if(str == "")return;
    tcpsocket->write(str.toUtf8().data());      //利用通信套接字向发送缓冲区写入数据
    ui->textEdit_2->clear();       //清空发送区
    qtchatmessage* messageW = new qtchatmessage(ui->listWidget->parentWidget()); //创建气泡
    QListWidgetItem* item = new QListWidgetItem(ui->listWidget);//创建小部件,选项控件
    dealMessage(messageW, item, str, qtchatmessage::User_Me);   //处理创建的气泡
    ui->listWidget->setCurrentRow(ui->listWidget->count()-1);   //移动到最后一行
}




然后客户端设置事件过滤器,来过滤键盘操作,设置快捷键回车来点击发送按钮
bool aWidget::eventFilter(QObject *target, QEvent *event){   //事件过滤器,不设置事件过滤器会被系统的函数自动过滤
    if(target == ui->textEdit_2)
    {
      if(event->type() == QEvent::KeyPress)         //按键盘事件
      {
             QKeyEvent *k = static_cast<QKeyEvent *>(event);
             if(k->key() == Qt::Key_Return||k->key() == Qt::Key_Enter)          //回车
             {
               aWidget::on_pushButton_clicked();
               return true;
             }
      }
    }
    return QWidget::eventFilter(target,event);
}



接受函数采用QT提供的虚函数接受消息处理
connect(tcpsocket, &QTcpSocket::readyRead,
            [=]()
            {
                QByteArray str = tcpsocket->readAll();   //获取对方发送的数据
                QString string = static_cast<QString>(str);   //强转
                if(record == 'n'){
                  if(string == "yes"){record = 'y'; w1.hide();this->show();}else return;
                }
                qtchatmessage* messageW = new qtchatmessage(ui->listWidget->parentWidget()); //创建气泡
                QListWidgetItem* item = new QListWidgetItem(ui->listWidget);//创建小部件,选项控件
                dealMessage(messageW, item, str, qtchatmessage::User_He);   //处理创建的气泡
                ui->listWidget->setCurrentRow(ui->listWidget->count()-1);   //移动到最后一行
            } );   //接受数据



客户端大致就这些。接下来总结一下服务端:
变量列表:QTcpServer *tcpserver;
    QTcpSocket *tcpsocket;
    char flag;   //判断是否连接
    char record;//判断是否登录
    void initMumber();
    void initMumber_1();
    void dealConnect();
    void dealMessage(int i);
    void dealRecord(int i);   //处理登录




创建了十三个通信套接字,一个监听套接字。标志位变量。首先初始化各变量,包括监听套接字。
void aWidget::initMumber(){
    tcpserver = new QTcpServer(this);
    tcpserver->listen(QHostAddress::Any, 6666);
    aWidget::initMumber_1();
}


void aWidget::initMumber_1(){
    for(int j = 0;j<13;j++){
      flag = 'n';
      record = 'n';
      tcpsocket = nullptr;
      }
}



服务端的监听套接字,监听新连接,触发信号调用处理函数,设置标志位变量为已连接。
void aWidget::dealConnect(){    //处理连接
    for(int i = 0;i < 13;i++){
      if(flag == 'n'){
          flag = 'y';
          tcpsocket = tcpserver->nextPendingConnection();   //连接客户端
          QString ip = tcpsocket->peerAddress().toString();//打印信息
          quint16 port = tcpsocket->peerPort();
          QString temp = QString("[%1:%2]:接入服务").arg(ip).arg(port);
          ui->textEdit->append(temp);   //向服务器显示连接信息
          connect(tcpsocket, &QTcpSocket::readyRead,[=](){aWidget::dealMessage(i);});//监听是否来信息//dealMessage函数的参数必须传局部变量,不能传全局变量。
          break;   //必须有break,否则会把通信套接字循环完的。                                             //随着i的变化Lambda表达式的函数体随着变化
      }
    }
    int b;    //不能写进for里面,因为有的退出了,套接字就不能用了
    for (b = 0;b < 13;b++) {
      if(flag == 'n')break;
    }
    if(b == 13)
      ui->textEdit->append("已经没有可用的套接字了");


}



入监听接受信息函数来分发信息,达到服务器转发信息的目的
void aWidget::dealMessage(int i){    //把消息分发给所有在线客户
      if(record == 'n'){
          aWidget::dealRecord(i);
          return;
      }
      QByteArray array = tcpsocket->readAll();    //获取内容
      for(int j = 0;j<13;j++)
      {
          if(flag != 'n'&&i != j)
          {
            tcpsocket->write(array);
          }
      }
}



基本上TCP通信采用QT开发的基本流程就这些,还有一些细节的处理,项目中对气泡的处理参考了一下网络上的代码。其他调用函数我没写进去,需要的小伙伴说一声,我抽空上传到百度网盘。由于项目代码中涉及到一些隐私就没全部上传,所有的图片均取自于网络,如有侵权,联系笔者,立刻删除。新人第一次发帖,大家多多照顾。目前大二小菜鸟,希望和大家共同进步。加油啦~。
代码如果哪里有问题,大佬勿喷,笔者还是大二在校生。联系笔者,一定跟大家交流,共同进步。


要乖,那些错过的,咱们不要了。

zhaopython 发表于 2020-9-9 11:39

如果改成有客户端请求链接,服务端再创建套接字监听更合理,客户端断开后删除对应套接字,这样改一下理论上就可以监听无数个端口,在你这个基础上加点代码就可以了,小小建议:lol

clickto 发表于 2020-6-17 20:56

做的不错啊!

seattle^-^ 发表于 2020-6-19 14:40

clickto 发表于 2020-6-17 20:56
做的不错啊!

害。小玩意儿,没啥技术含量。

小鱼又咸又辣 发表于 2020-6-29 09:06

写的不错

seattle^-^ 发表于 2020-6-29 10:55

小鱼又咸又辣 发表于 2020-6-29 09:06
写的不错

尴尬尴尬{:1_924:}      {:1_924:}

白竹 发表于 2020-7-6 22:01

哥有代码吗

senlinyiye 发表于 2020-7-6 23:11

大哥,厉害了

hhxxttxs 发表于 2020-8-3 20:26

好样的,继续努力!

大侠在路上 发表于 2020-9-9 12:26

感谢楼主分享,我觉得已经很棒了。
页: [1] 2
查看完整版本: QT开发TCP协议的通信软件