QT示例:基于TCP 点对多通讯(server,clients)

  • 一、服务器server
  • 二、客户端Client

下载:基于TCP 点对多Socket通讯

一、服务器server

因为对于客户端来说,只能连接一个服务器。而对于服务器来说,它是面向多连接的,如何协调处理多客户端连接就显得尤为重要。

  • 注意问题:
    每个新加入的客户端,服务器给其分配一个SocketDescriptor后,就会emit newConnection()信号,但分配好的SocketDecriptor并没有通过newConnection()信号传递,所以用户得不到这个客户端标识SocketDescriptor。同样的,每当服务器收到新的消息时,客户端会emit readReady()信号,然而readReady()信号也没有传递SocketDescriptor, 这样的话,服务器端即使接收到消息,也不知道这个消息是从哪个客户端发出的。
  • 解决的方法:
  1. 通过重写==[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor)==,获取soketDescriptor。自定义TcpClient类继承QTcpSocket,并将获得的soketDescriptor作为类成员。 这个方法的优点是:可以获取到soketDescriptor,灵活性高。缺点是:需要重写函数、自定义类。
  2. 在newConnection()信号对应的槽函数中,通过QTcpSocket *QTcpServer::nextPendingConnection()函数获取 新连接的客户端:Returns the next pending connection as a connected QTcpSocket object. 虽然仍然得不到soketDescriptor,**但可以通过QTcpSocket类的peerAddress()和peerPort()成员函数获取客户端的IP和端口号,同样是唯一标识。 **优点:无需重写函数和自定义类,代码简洁。缺点:无法获得SocketDecriptor,灵活性差。

本文示例为第二种方法:
1).pro 添加:

QT       += network

2)主函数 main.cpp 添加:

#include "mytcpserver.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MyTcpServer w;w.show();return a.exec();
}

3)MyTcpServer.h 添加:

#include "mytcpserver.h"
#include "ui_mytcpserver.h"MyTcpServer::MyTcpServer(QWidget *parent) :QMainWindow(parent),ui(new Ui::MyTcpServer)
{ui->setupUi(this);//  一 、创建QTcpSever对象;tcpServer = new QTcpServer(this);ui->edtIP->setText(QNetworkInterface().allAddresses().at(1).toString());   //获取本地IPui->btnConnect->setEnabled(true);ui->btnSend->setEnabled(false);// 设置默认按钮样式ui->btnConnect->setStyleSheet("");connect(tcpServer, &QTcpServer::newConnection, this, &MyTcpServer::NewConnectionSlot);
}MyTcpServer::~MyTcpServer()
{delete ui;
}// 二、监听--断开
void MyTcpServer::on_btnConnect_clicked()
{if(ui->btnConnect->text()=="监听"){bool ok = tcpServer->listen(QHostAddress::Any, ui->edtPort->text().toInt());if(ok){ui->btnConnect->setText("断开");ui->btnConnect->setStyleSheet("color: red;");ui->btnSend->setEnabled(true);}}else{for(int i=0; i<tcpClient.length(); i++) // 断开所有连接{tcpClient[i]->disconnectFromHost();bool ok = tcpClient[i]->waitForDisconnected(1000);if(!ok){// 处理异常QMessageBox::warning(this, tr("错误"),tr("断开连接失败!"), QMessageBox::Ok);}tcpClient.removeAt(i);  // 从保存的客户端列表中取去除}tcpServer->close();     // 不再监听端口ui->btnConnect->setText("监听");ui->btnConnect->setStyleSheet("");ui->btnSend->setEnabled(false);}
}// 三、新连接建立的槽函数
void MyTcpServer::NewConnectionSlot()
{currentClient = tcpServer->nextPendingConnection();tcpClient.append(currentClient);ui->cbxConnection->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1]).arg(currentClient->peerPort()));connect(currentClient, &QTcpSocket::readyRead, this,  &MyTcpServer::ReadData);connect(currentClient, &QTcpSocket::disconnected, this,  &MyTcpServer::disconnectedSlot);
}// 四、客户端数据可读信号,对应的读数据槽函数
void MyTcpServer::ReadData()
{// 由于readyRead信号并未提供SocketDecriptor,所以需要遍历所有客户端for(int i=0; i<tcpClient.length(); i++){QByteArray buffer = tcpClient[i]->readAll();if(buffer.isEmpty())    // 客户端 数据为空,则跳过continue;// 客户端有数据则 获取IP 和端口static QString IP_Port, IP_Port_Pre;IP_Port = tr("[%1:%2]:").arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]).arg(tcpClient[i]->peerPort());// 若此次消息的地址与上次不同,则需显示此次消息的客户端地址if(IP_Port != IP_Port_Pre)ui->edtRecv->append(IP_Port);ui->edtRecv->append(buffer);//更新ip_portIP_Port_Pre = IP_Port;}
}// 五、断开连接的槽函数
void MyTcpServer::disconnectedSlot()
{//由于disconnected信号并未提供SocketDescriptor,所以需要遍历寻找for(int i=0; i<tcpClient.length(); i++){if(tcpClient[i]->state() == QAbstractSocket::UnconnectedState){// 删除存储在combox中的客户端信息ui->cbxConnection->removeItem(ui->cbxConnection->findText(tr("%1:%2").arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]).arg(tcpClient[i]->peerPort())));// 删除存储在tcpClient列表中的客户端信息tcpClient[i]->destroyed();tcpClient.removeAt(i);}}
}// 六、发送数据
void MyTcpServer::on_btnSend_clicked()
{QString data = ui->edtSend->toPlainText();if(data == "")return;    // 文本输入框为空时//全部连接if(ui->cbxConnection->currentIndex() == 0){for(int i=0; i<tcpClient.length(); i++)tcpClient[i]->write(data.toLatin1()); //qt5除去了.toAscii()}//指定连接else{QString clientIP = ui->cbxConnection->currentText().split(":")[0];  // IP 地址int clientPort = ui->cbxConnection->currentText().split(":")[1].toInt();    // port 端口号
//        qDebug() << clientIP;
//        qDebug() << clientPort;for(int i=0; i<tcpClient.length(); i++){if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==clientIP&& tcpClient[i]->peerPort()==clientPort){tcpClient[i]->write(data.toLatin1());return; //ip:port唯一,无需继续检索}}}
}// 清楚窗口
void MyTcpServer::on_btnClear_clicked()
{ui->edtRecv->clear();
}

5)界面 mytcpserver.ui

二、客户端Client

1).pro 添加:

QT       += network

2)主函数 main.cpp 添加:

#include "mytcpclient.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MyTcpClient w;w.show();return a.exec();
}

3)MyTcpClient.h 添加:

#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>namespace Ui {class MyTcpClient;
}class MyTcpClient : public QMainWindow
{Q_OBJECTpublic:explicit MyTcpClient(QWidget *parent = 0);~MyTcpClient();private:Ui::MyTcpClient *ui;QTcpSocket *tcpClient;private slots://客户端槽函数void ReadData();void ReadError(QAbstractSocket::SocketError);void on_btnConnect_clicked();void on_btnSend_clicked();void on_pushButton_clicked();
};

4)MyTcpClient.cpp 添加:

#include "mytcpclient.h"
#include "ui_mytcpclient.h"MyTcpClient::MyTcpClient(QWidget *parent) :QMainWindow(parent),ui(new Ui::MyTcpClient)
{ui->setupUi(this);// 一、初始化TCP客户端tcpClient = new QTcpSocket(this);   //实例化tcpClienttcpClient->abort();                 //取消原有连接ui->btnConnect->setEnabled(true);ui->btnSend->setEnabled(false);connect(tcpClient, &QTcpSocket::readyRead, this, &MyTcpClient::ReadData);connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(ReadError(QAbstractSocket::SocketError)));
}MyTcpClient::~MyTcpClient()
{delete ui;
}// 二、连接
void MyTcpClient::on_btnConnect_clicked()
{if(ui->btnConnect->text()=="连接"){tcpClient->connectToHost(ui->edtIP->text(), ui->edtPort->text().toInt());if (tcpClient->waitForConnected(1000))  // 连接成功则进入if{}{ui->btnConnect->setText("断开");ui->btnSend->setEnabled(true);}}else{tcpClient->disconnectFromHost();if (tcpClient->state() == QAbstractSocket::UnconnectedState || tcpClient->waitForDisconnected(1000))  //已断开连接则进入if{}{ui->btnConnect->setText("连接");ui->btnSend->setEnabled(false);}}
}// 三、读取数据
void MyTcpClient::ReadData()
{QByteArray buffer = tcpClient->readAll();if(!buffer.isEmpty()){ui->edtRecv->append(buffer);}
}// 四、发送数据
void MyTcpClient::on_btnSend_clicked()
{QString data = ui->edtSend->toPlainText();if(data != ""){tcpClient->write(data.toLatin1()); //qt5出去了.toAscii()}
}// 连接错误信息处理
void MyTcpClient::ReadError(QAbstractSocket::SocketError)
{tcpClient->disconnectFromHost();ui->btnConnect->setText(tr("连接"));QMessageBox msgBox;msgBox.setText(tr("failed to connect server because %1").arg(tcpClient->errorString()));msgBox.exec();
}// 清空按钮
void MyTcpClient::on_pushButton_clicked()
{ui->edtRecv->clear();
}

2)mytcpclient.ui 添加


参考博客:
QT 之TCP网络编程

QT示例:基于TCP 点对多Socket通讯(server,clients)相关推荐

  1. Android端与服务端基于TCP/IP协议的Socket通讯

    什么是TCP/IP协议? 百度百科的解释:Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议, ...

  2. 基于tcp和udp的socket实现

    2019独角兽企业重金招聘Python工程师标准>>> 本文介绍如何用Java实现Socket编程.首先介绍Java针对Socket编程提供的类,以及它们之间的关系.然后分别针对TC ...

  3. 基于TCP和UDP的socket

    为什么学习socket 你自己现在完全可以写一些小程序了,但是前面的学习和练习,我们写的代码都是在自己的电脑上运行的,虽然我们学过了模块引入,文件引入import等等,我可以在程序中获取到另一个文件的 ...

  4. 基于MDKA5D31-EK_T70开发板的QT示例-demo08:TCP通讯

    By Mcuzone 硬件平台:MDKA5D31-EK_T70开发板  QT版本:4.8.5  简要说明:本示例主要演示应用程序作为UDP服务器和客户端的通讯过程. 应用程序运行效果: 一.网络连接: ...

  5. C++基于TCP和UDP的socket通信

    TCP和UDP属于传输层协议.其中TCP提供IP环境下的数据可靠传输,它事先为要发送的数据开辟好连接通道(三次握手),然后再进行数据发送:而UDP则不为IP提供可靠性,一般用于实时的视频流传输,像rt ...

  6. 基于TCP原理,采用Socket通信技术,实现聊天室

    文章目录 案例一 案例二 案例三 案例四 案例五 相关案例 案例一 Client.java package SocketCode;import java.awt.Color; import java. ...

  7. 即时通讯系统————基于TCP协议的C/S架构(Server)

    服务器端 #ifndef SERVER_H #define SERVER_H#include <sys/socket.h> #include <netinet/in.h> #i ...

  8. JAVA Socket 底层是怎样基于TCP/IP 实现的???

    首先必须明确:TCP/IP模型中有四层结构:       应用层(Application Layer).传输层(Transport  Layer).网络层(Internet Layer  ).链路层( ...

  9. 超硬核!!!一篇文章搞定TCP、UDP、Socket、HTTP(详细网络编程内容+现实解释三次握手四次挥手+代码示例)【网络编程 1】

    TCP.UDP.Socket 一天面试的经验: 什么是网络编程 网络编程中两个主要的问题 网络协议是什么 为什么要对网络协议分层 计算机网络体系结构 1 TCP / UDP 1.1 什么是TCP/IP ...

最新文章

  1. sql相同顺序法和一次封锁法_率土之滨追击战法攻略
  2. android 勿扰模式代码,Android N Zen Mode (勿扰模式)设置流程
  3. [zz]linux下DHCP服务器配置(使用Ubuntu LTS 8.04)
  4. Linux 中使用 sort 指令分组排序详解
  5. UITextField对字符串的个数限制
  6. 常用numpy模块用法总结
  7. Linux C/C++ —— intent 工具,time 命令
  8. MFC UpdateData
  9. 选择排序之C++实现
  10. linux 压缩文件的命令总结
  11. string成员函数
  12. 带通滤波器的matlab程序设计
  13. ESP-8266wifi模块获取网络实时时间
  14. css 文本和div垂直居中方法汇总
  15. linux测试upnp,UPnP linux新手入门
  16. 德州农工大学计算机专业研究生,德州农工大学计算机专业
  17. ios markdown 解析_认识与入门 Markdown,Markdown教程
  18. 计算机如何与机顶盒连接网络连接网络连接,如何将机顶盒连接到计算机
  19. lseek和文件末尾
  20. 关于5G时延的深度解读,非常详尽

热门文章

  1. css scale 元素不放大,列元素上的CSS 3动画“transform:scale”对chrome不起作用
  2. sql server排序慢_SQL 查询调优之 where 条件排序字段以及 limit 使用索引的奥秘
  3. 乐学python_【IT专家】铁乐学python
  4. 小米10pro第二个摄像头下面_小米10至尊纪念版、小米10 Pro对比评测:至尊版“至尊”在哪里?...
  5. 论文阅读笔记(五)【ACL 2021】Answering Ambiguous Questions through Generative Evidence Fusion and Round-Trip P
  6. 如何做机器学习项目规划?一个事半功倍的checklist
  7. 论文浅尝 | PairRE: 通过成对的关系向量实现知识图谱嵌入
  8. 论文浅尝 - IJCAI2020 | KGNN:基于知识图谱的图神经网络预测药物与药物相互作用...
  9. NLP-美团技术团队(搜索-推荐-召回排序-Bert)
  10. 268G+训练好的word2vec模型(中文词向量)