8.9更新说明

如下图所示,支持十六进制收发,下载地址已经更新.源码下载地址:https://download.csdn.net/download/qq_37997682/11504836


在上章 48.QT-网络通信讲解1,我们学习了网络通信基础后,本章便来实战一篇.

PS:支持客户端和服务器,提供源码,并且服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.

1.效果图所下所示:

如下图所示,当服务器状态下,如果有客户端连入,会提示客户端信息:

2.效果操作

客户端操作:

服务器操作:

从上面操作可以看出,服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.

3.首先创建UI

4.注意事项

不管是服务器还是客户端,都可以通过peerAddress()和peerPort()来获取目标地址和目标端口

4.1服务器监听时

比如服务器,则可以通过QTcpSocket的peerAddress()则可以获取连入的客户端地址

也可以通过children()来获取所有连入的客户端(需要注意的是也会获取到服务器本身的tcp地址和端口),示例如下:

  1. QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
  2. foreach (QTcpSocket *tcp, m_tcps)
  3. {
  4. qDebug() << "Address:" << tcp->peerAddress ();
  5. qDebug() << "Port:" << tcp->peerPort ();
  6. }

如果我们只向连入的客户端某个端口发送数据时,就可以通过上面的方式筛选出来.

这样做的话如果觉得很麻烦,也可以将之前连接上的客户端存到QList里再进行筛选.

4.2 QTcpSocket步骤

  • 首先通过connectToHost()来连接服务器.
  • 然后调用waitForConnected()来判断是否连接服务器超时
  • 当我们接收到服务器数据的时候,则会发出readyRead()信号,然后再进行read ()读取发来的数据
  • 发送数据时,则调用write()函数进行发送,当bytesWritten()信号函数触发时,便可以获取成功发送的数据长度.

注意:如果read到的数据长度量不是自己想要的,此时我们便可以通过bytesAvailable()来读取接收到的数据长度量.当达到多少时,再进行read ()读取.

4.3 QTcpServer步骤

  • 首先通过listen(QHostAddress::AnyIPv4, port)来监听所有来自IPV4的客户端
  • 当有新的客户端连接服务器的时候,会自动触发newConnection()信号函数,然后我们可以通过通过QTcpSocket * nextPendingConnection()成员函数来获取当前连接上的新的客户端类.然后再对QTcpSocket来进行信号槽绑定
  • 当客户端发来数据的时候,则可以通过我们定义的onServerDataReady()来读取数据
  • 当我们向某个连接的客户端发送数据时,则通过m_server.findChildren()来筛选出来,然后write即可.

5.代码介绍

5.1  头文件介绍

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QTcpSocket>
  6. #include <QTcpServer>
  7. #include <QMessageBox>
  8.  
  9. namespace Ui {
  10. class Widget;
  11. }
  12.  
  13. class Widget : public QWidget
  14. {
  15. Q_OBJECT
  16.  
  17. QTcpSocket m_client;
  18. QTcpServer m_server;
  19. QString targetAddr;
  20. int targetPort;
  21. public:
  22. explicit Widget(QWidget *parent = );
  23. ~Widget();
  24. private slots:
  25. void on_btn_switch_clicked();
  26. void on_tcpMode_currentIndexChanged(int index);
  27. void on_btn_send_clicked(); //发送按钮
  28. void on_betn_clear_clicked(); //清空按钮
  29.  
  30. //客户端槽函数
  31. void onClientConnected();
  32. void onClientDisconnected();
  33. void onClientDataReady();
  34. void onClientBytesWritten(qint64 bytes);
  35. void onClientErr(QAbstractSocket::SocketError socketError);
  36.  
  37. //服务器槽函数
  38. void onServerNewConnection();
  39. void onServerConnected();
  40. void onServerDisconnected();
  41. void onServerDataReady();
  42. void onServerBytesWritten(qint64 bytes);
  43. private:
  44. void startConnect(bool ison);
  45.  
  46. void initClientSignals(); //初始化客户端信号槽
  47. bool startClient(); //启动客户端
  48.  
  49. void initServerSignals(); //初始化客户端信号槽
  50. bool startServer(); //启动服务器
  51.  
  52. Ui::Widget *ui;
  53. };
  54.  
  55. #endif // WIDGET_H

5.2  widget.cpp介绍

该cpp主要是用来处理界面操作的函数

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. Widget::Widget(QWidget *parent) :
  5. QWidget(parent),
  6. ui(new Ui::Widget)
  7. {
  8. ui->setupUi(this);
  9. startConnect(false);
  10. on_tcpMode_currentIndexChanged();
  11. initClientSignals(); //初始化客户端信号槽
  12. initServerSignals(); //初始化服务器信号槽
  13.  
  14. //限制只能数字输入
  15. QRegExp regx("[0-9]+$");
  16. QValidator *validator = new QRegExpValidator(regx, this );
  17. ui->ipAddr1->setValidator( validator );
  18. ui->ipAddr2->setValidator( validator );
  19. ui->ipAddr3->setValidator( validator );
  20. ui->ipAddr4->setValidator( validator );
  21. ui->ipPort->setValidator( validator );
  22. }
  23. void Widget::on_tcpMode_currentIndexChanged(int index)
  24. {
  25. if(index==) //clent
  26. {
  27. ui->ipPortLabel->setText("服务器端口号:");
  28. ui->ipAddLabel->show();
  29. ui->ipAdds->show();
  30. ui->targetLabel->hide();
  31. ui->targetObject->hide();
  32. }
  33. else
  34. {
  35. ui->ipAddLabel->hide();
  36. ui->ipAdds->hide();
  37. ui->ipPortLabel->setText("本地端口号:");
  38. }
  39. }
  40.  
  41. void Widget::on_btn_switch_clicked() //切换连接开关
  42. {
  43. bool ret;
  44. if(ui->btn_switch->text()=="打开连接")
  45. {
  46. if(ui->tcpMode->currentIndex()==) //启动客户端
  47. ret=startClient() ;
  48. else
  49. ret=startServer();
  50. if(ret)
  51. startConnect(true);
  52. }
  53. else
  54. {
  55. if(ui->tcpMode->currentIndex()==) //启动客户端
  56. m_client.close();
  57. else
  58. {
  59. if( m_server.isListening() )
  60. {
  61. QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
  62. foreach (QTcpSocket *tcp, m_tcps)
  63. {
  64. tcp->close();
  65. }
  66. m_server.close();
  67. }
  68. }
  69. startConnect(false);
  70. }
  71. }
  72.  
  73. void Widget::startConnect(bool ison)
  74. {
  75. if(!ison)
  76. {
  77. ui->btn_switch->setStyleSheet("color:blue;border: 1px solid blue");
  78. ui->btn_switch->setText("打开连接");
  79.  
  80. //使能
  81. ui->ipAddr1->setEnabled(true);
  82. ui->ipAddr2->setEnabled(true);
  83. ui->ipAddr3->setEnabled(true);
  84. ui->ipAddr4->setEnabled(true);
  85. ui->tcpMode->setEnabled(true);
  86. ui->ipPort->setEnabled(true);
  87. ui->localPort->setText("");
  88. }
  89. else
  90. {
  91. ui->btn_switch->setStyleSheet("color:red;border: 1px solid red");
  92. ui->btn_switch->setText("关闭连接");
  93. //失能
  94. ui->ipAddr1->setEnabled(false);
  95. ui->ipAddr2->setEnabled(false);
  96. ui->ipAddr3->setEnabled(false);
  97. ui->ipAddr4->setEnabled(false);
  98. ui->tcpMode->setEnabled(false);
  99. ui->ipPort->setEnabled(false);
  100. targetAddr="";
  101. targetPort=;
  102. ui->sendLenLabel->setText("");
  103. }
  104. }
  105.  
  106. void Widget::on_betn_clear_clicked()
  107. {
  108. ui->recvEdit->clear();
  109. targetAddr="";
  110. targetPort=;
  111. }
  112.  
  113. void Widget::on_btn_send_clicked()
  114. {
  115. if(ui->btn_switch->text()!="打开连接")
  116. {
  117. if(ui->tcpMode->currentIndex()==) //客户端
  118. {
  119. m_client.write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
  120. }
  121. else
  122. {
  123. if(ui->targetObject->currentText()!="所有对象")
  124. {
  125. QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
  126. foreach (QTcpSocket *tcp, m_tcps)
  127. {
  128. if(ui->targetObject->currentText() == tcp->objectName())
  129. {
  130. tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
  131. break;
  132. }
  133. }
  134. }
  135. else //所有连接上的客户端都发送一遍
  136. {
  137. QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
  138. foreach (QTcpSocket *tcp, m_tcps)
  139. {
  140. tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
  141. }
  142. }
  143. }
  144. }
  145. }
  146.  
  147. Widget::~Widget()
  148. {
  149. QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
  150. foreach (QTcpSocket *tcp, m_tcps)
  151. {
  152. tcp->close();
  153. }
  154. if(m_client.isOpen())
  155. {
  156. m_client.close();
  157. qDebug()<<"m_client close";
  158. }
  159. delete ui;
  160. }

5.3 clentHandler.cpp介绍

该cpp主要用来处理客户端操作相关的文件.

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. void Widget::initClientSignals() //初始化客户端信号槽
  5. {
  6. connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected()));
  7. connect(&m_client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
  8. connect(&m_client, SIGNAL(readyRead()), this, SLOT(onClientDataReady()));
  9. connect(&m_client, SIGNAL(bytesWritten(qint64)), this, SLOT(onClientBytesWritten(qint64)));
  10. connect(&m_client, SIGNAL(error(QAbstractSocket::SocketError )), this, SLOT(onClientErr(QAbstractSocket::SocketError)));
  11. }
  12. bool Widget::startClient() //启动客户端
  13. {
  14. QString ip = QString("%1.%2.%3.%4").arg(ui->ipAddr1->text()).arg(ui->ipAddr2->text()).arg(ui->ipAddr3->text()).arg(ui->ipAddr4->text());
  15. qDebug()<<ip;
  16. m_client.connectToHost(ip, ui->ipPort->text().toInt());
  17. if(m_client.waitForConnected())
  18. {
  19. return true;
  20. }
  21. else
  22. {
  23. QMessageBox::information(this,"提示",QString("连接超时"),QMessageBox::Ok);
  24. return false;
  25. }
  26. }
  27.  
  28. void Widget::onClientConnected()
  29. {
  30. startConnect(true);
  31. QMessageBox::information(this,"提示","连接成功",QMessageBox::Ok);
  32. ui->localPort->setText(QString("%1").arg(m_client.localPort())); //显示本地端口号
  33. }
  34. void Widget::onClientDisconnected()
  35. {
  36. startConnect(false);
  37. QMessageBox::information(this,"提示","断开完成",QMessageBox::Ok);
  38. }
  39. void Widget::onClientDataReady()
  40. {
  41. if(m_client.peerAddress().toString()!=targetAddr || m_client.peerPort()!=targetPort )
  42. {
  43. targetAddr = m_client.peerAddress().toString();
  44. targetPort = m_client.peerPort();
  45. ui->recvEdit->insertPlainText("[接受来自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
  46. }
  47. ui->recvEdit->moveCursor(QTextCursor::End);
  48. ui->recvEdit->insertPlainText(QString::fromLocal8Bit(m_client.readAll())+"\r\n");
  49. }
  50. void Widget::onClientBytesWritten(qint64 bytes)
  51. {
  52. qDebug() << "onBytesWritten:" << bytes;
  53. ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
  54. }
  55.  
  56. void Widget::onClientErr(QAbstractSocket::SocketError socketError)
  57. {
  58. qDebug()<<"onClientErr:"<<socketError;
  59. m_client.close();
  60. startConnect(false);
  61. QMessageBox::information(this,"提示",QString("连接失败:%1").arg((int)socketError),QMessageBox::Ok);
  62. }

5.4 serverHandler.cpp介绍

该cpp主要用来处理服务器操作相关的文件

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. void Widget::initServerSignals() //初始化信号槽
  5. {
  6. connect(&m_server, SIGNAL(newConnection()), this, SLOT(onServerNewConnection()));
  7. }
  8. bool Widget::startServer() //启动服务器
  9. {
  10. if(m_server.listen(QHostAddress::AnyIPv4,ui->ipPort->text().toInt())) //只监听IPV4的所有客户端
  11. {
  12. ui->targetLabel->show();
  13. ui->targetObject->show();
  14. ui->localPort->setText(QString("%1").arg(m_server.serverPort()));
  15. return true;
  16. }
  17. else
  18. return false;
  19. }
  20. void Widget::onServerNewConnection()
  21. {
  22. qDebug() << "onNewConnection";
  23. QTcpSocket* tcp = m_server.nextPendingConnection(); //获取新的客户端信息
  24. QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
  25. ui->targetObject->addItem(info);
  26. QMessageBox::information(this,"提示",QString("新的客户端连入:%1").arg(info),QMessageBox::Ok);
  27. tcp->setObjectName(info); //设置名称,方便查找
  28. connect(tcp, SIGNAL(connected()), this, SLOT(onServerConnected()));
  29. connect(tcp, SIGNAL(disconnected()), this, SLOT(onServerDisconnected()));
  30. connect(tcp, SIGNAL(readyRead()), this, SLOT(onServerDataReady()));
  31. connect(tcp, SIGNAL(bytesWritten(qint64)), this, SLOT(onServerBytesWritten(qint64)));
  32. }
  33. void Widget::onServerConnected()
  34. {
  35.  
  36. }
  37.  
  38. void Widget::onServerDisconnected()
  39. {
  40. QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
  41. if( tcp != NULL ) //从连接对象中移除掉
  42. {
  43. qDebug() << "onServerDisconnected";
  44. qDebug() << "Local Address:" << tcp->peerAddress();
  45. qDebug() << "Local Port:" << tcp->peerPort();
  46. QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
  47. QMessageBox::information(this,"提示",QString("客户端断开连接:%1").arg(info),QMessageBox::Ok);
  48.  
  49. int index = ui-> targetObject ->findText(info);
  50. if(index>=)
  51. ui->targetObject->removeItem(index);
  52. }
  53. }
  54. void Widget::onServerDataReady()
  55. {
  56. QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
  57. if(tcp->peerAddress().toString()!=targetAddr || tcp->peerPort()!=targetPort )
  58. {
  59. targetAddr = tcp->peerAddress().toString();
  60. targetPort = tcp->peerPort();
  61. ui->recvEdit->insertPlainText("[接受来自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
  62. }
  63. ui->recvEdit->moveCursor(QTextCursor::End);
  64. ui->recvEdit->insertPlainText(QString::fromLocal8Bit(tcp->readAll())+"\r\n");
  65. }
  66. void Widget::onServerBytesWritten(qint64 bytes)
  67. {
  68. qDebug() << "onBytesWritten:" << bytes;
  69. ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
  70. }

49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)的更多相关文章

  1. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  2. Java网络编程之TCP、UDP

    Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载.   ...

  3. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

  4. [深入浅出WP8.1(Runtime)]网络编程之HttpClient类

    12.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,我们还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类 ...

  5. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...

  6. QT核心编程之Qt线程 (c)

    QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...

  7. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  8. 网络编程之UDP编程

    网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了Data ...

  9. 网络编程之TCP编程

    网络编程之TCP编程 前面已经介绍过关于TCP协议的东西,这里不做赘述.Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生I ...

随机推荐

  1. MySQL 全文索引实现简单版搜索引擎

    目录 前言 使用 三种类型的全文搜索方式 相关参数 测试innodb引擎使用全文索引 准备 使用默认方式创建全文索引 使用ngram分词解析器创建全文索引 结论 前言 只有Innodb和myisam存 ...

  2. 常用的方法论-PDCA

  3. hihoCoder 1312:搜索三·启发式搜索(A* + 康托展开)

    题目链接 题意 中文题意 思路 做这题的前置技能学习 康托展开 这个东西我认为就是在排列组合问题上的Hash算法,可以压缩空间. A*搜索. 这里我使用了像k短路一样的做法,从最终状态倒回去预处理一遍 ...

  4. HDU 4055:Number String(DP计数)

    http://acm.hdu.edu.cn/showproblem.php?pid=4055 题意:给一个仅包含‘I','D','?'的字符串,’I'表示前面的数字比后面的数字要小(Increase升 ...

  5. Java面试题 equals()与"=="的区别?

    面试官:请问 equals() 和 "==" 有什么区别? 应聘者: equals()方法用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object ...

  6. 【tf.keras】在 cifar 上训练 AlexNet,数据集过大导致 OOM

    cifar-10 每张图片的大小为 32×32,而 AlexNet 要求图片的输入是 224×224(也有说 227×227 的,这是 224×224 的图片进行大小为 2 的 zero paddin ...

  7. python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)

    简介 上一篇,我们虽然已经将生成的最新的测试报告发出去了,但是MIMEText 只能发送正文,无法带附件,因此我还需要继续改造我们的代码,实现可以发送带有附件的邮件.发送带附件的需要导入另外一个模块 ...

  8. Java编程思想:内部类其他知识点

    public class Test { public static void main(String[] args) { // InheritInner.test(); // EggTest.test ...

  9. PTA 打印沙漏

    https://pintia.cn/problem-sets/17/problems/260 #include <bits/stdc++.h> using namespace std; i ...

  10. [leetcode] 147. Insertion Sort List (Medium)

    原题 别人的思路 非常简洁 function ListNode(val) { this.val = val; this.next = null; } /** * @param {ListNode} h ...