Qt开发TCPIP网络通信
TCP/IP通信(即SOCKET通信)是通过网线将 服务器Server端 和 客户机Client端 进行连接,在遵循ISO/OSI模型的四层层级构架的基础上通过TCP/IP协议建立的通讯。控制器可以设置为服务器端或客户端。
关于TCP/IP协议可详看:TCP/IP协议详解 - 知乎 (zhihu.com)
总的来说,TCP/IP通讯有两个部分: 客户端 和 服务器 QTcpServer(监听套接字) 和 QTcpSocket(通讯套接字)
监听套接字,顾名思义,监听关于各种通讯的状态,一旦进行通讯,监听套接字会启动通讯套接字,进行通讯
客户端使用connectToHost函数主动连接服务器后,服务器会触发newConnectio这个槽函数,并进行取出QTcpServer(监听套接字),将相关内容取出并赋给QTcpSocket(通讯套接字)。
客户端向服务器发送数据,触发readyRead(),进行处理,彼此传递时,原理都是这样的。
对双方来说都起作用的部分: 一旦建立连接,就会触发connected,服务器特殊一点,触发的是newConnectio 互传数据也是一样的,一旦接受到,就会触发readyread
服务器中,需要监听套接字以及通讯套接字,监听套接字用于监听客户端是否给服务器发送请求
本篇博文做了初步的学习与尝试,编写了一个客户端和服务器基于窗口通信以及文件传输的小例程。 一,客户端
客户端的代码比服务器稍简单,总的来说,使用QT中的 QTcpSocket类 与服务器进行通信只需要以下5步:
(1)创建QTcpSocket套接字对象 socket = new QTcpSocket(this);
(2)使用这个对象连接服务器 QString ip = ui.lineEdit_ip->text();//获取ip int port = ui.lineEdit_2->text().toInt();//获取端口数据 socket->connectToHost(ip, port);
(3)使用write函数向服务器发送数据 QByteArray data = ui.lineEdit_3->text().toUtf8();//获取lineEdit控件中的数据并发送给服务器 socket->write(data);
(4)当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据 connect(socket, &QTcpSocket::readyRead, this, &QTcpClinet::ReadData); void QTcpClinet::ReadData() { QByteArray buf = socket->readAll(); ui.textEdit->append(buf); }
(5)断开与服务器的连接(关于close()和disconnectFromHost()的区别,可以按F1看帮助) socket->disconnectFromHost();
客户端例程:(新建一个qt项目QTcpClinet(客户机)) ui界面
本地回路ip:127.0.0.1 可以连接到本地ip(电脑内部循环的ip)
如果要和局域网其他ip连接 -> 在运行(win+R)+cmd+ipconfig ->ipv4地址 查看本机ip
点击领取Qt学习资料+视频教程~「链接」
QTcpClinet.h #include #include "ui_QTcpClinet.h" #include"QTcpSocket.h" #pragma execution_character_set("utf-8") class QTcpClinet : public QWidget { Q_OBJECT public: QTcpClinet(QWidget *parent = Q_NULLPTR); ~QTcpClinet(); public slots: void on_btn_connect_clicked(); void ReadData(); void on_btn_push_clicked(); private: Ui::QTcpClinetClass ui; QTcpSocket* socket;//创建socket指针 };QTcpClinet.cpp #include "QTcpClinet.h" QTcpClinet::QTcpClinet(QWidget *parent) : QWidget(parent) { ui.setupUi(this); socket = new QTcpSocket(this); } QTcpClinet::~QTcpClinet() { delete this->socket;//回收内存 } void QTcpClinet::on_btn_connect_clicked() { if (ui.btn_connect->text()==tr("连接服务器")) { QString ip = ui.lineEdit_ip->text();//获取ip int port = ui.lineEdit_2->text().toInt();//获取端口数据 //取消已有的连接 socket->abort(); //连接服务器 socket->connectToHost(ip, port); bool isconnect = socket->waitForConnected();//等待直到连接成功 //如果连接成功 if (isconnect) { ui.textEdit->append("The connection was successful!!"); ui.btn_push->setEnabled(true);//按钮使能 //修改按键文字 ui.btn_connect->setText("断开服务器连接"); //接收缓冲区(服务器)信息 connect(socket, &QTcpSocket::readyRead, this, &QTcpClinet::ReadData); } else { ui.textEdit->append("The connection falied!!"); } } else { //断开连接 socket->disconnectFromHost(); ui.btn_connect->setText("连接服务器"); ui.btn_push->setEnabled(false);//关闭发送按钮使能 } } //接收缓冲区信息函数 void QTcpClinet::ReadData() { QByteArray buf = socket->readAll(); ui.textEdit->append(buf); } //发送按钮事件 void QTcpClinet::on_btn_push_clicked() { QByteArray data = ui.lineEdit_3->text().toUtf8();//获取lineEdit控件中的数据并发送给服务器 socket->write(data); //判断是否写入成功 bool iswrite = socket->waitForBytesWritten(); if (iswrite) { //写入成功 } else { //没有写入成功 } }二,服务器(需要一直运行哦)
服务器除了使用到了 QTcpSocket类 ,还需要用到 QTcpSever类 。即便如此,也只是比客户端复杂一点点,用到了6个步骤:
(1)创建QTcpSever对象 server = new QTcpServer(this);
(2)侦听一个端口,使得客户端可以使用这个端口访问服务器 server->listen(QHostAddress::Any, 6677);//监听所有ip和6677端口
(3)当服务器被客户端访问时,会发出newConnection()信号,因此为该信号添加槽函数,并用一个QTcpSocket对象接受客户端访问 connect(server, &QTcpServer::newConnection, this, &TcpServer::ClientConnect); void TcpServer::ClientConnect() { //解析所有客户连接 while (server->hasPendingConnections()) { //连接上后通过socket(QTcpSocket对象)获取连接信息 socket = server->nextPendingConnection(); QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort());//监听客户端是否有消息发送 connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1); } }
(4)使用socket的write函数向客户端发送数据 socket->write(data);
(5)当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据 //监听客户端是否有消息发送 connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1); //获取客户端向服务器发送的信息 void TcpServer::ReadData1() { QByteArray buf = socket->readAll();//readAll最多接收65532的数据 QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort()); ui.textEdit_server->append(str +QString(buf)); //socket->write("ok");//服务器接收到信息后返回一个ok }
(6)取消侦听 server->close();
服务器例程:(添加一个新的qt项目TcpServer(服务器)) ui界面 TcpServer.h #include #include"ui_TcpServer.h" #include"qtcpserver.h" #include"qtcpsocket.h" class TcpServer : public QWidget { Q_OBJECT public: TcpServer(QWidget *parent = Q_NULLPTR); ~TcpServer(); public slots: void on_btn_server_clicked(); void on_btn_listen_clicked(); private: Ui::TcpServerClass ui; QTcpServer* server; QTcpSocket* socket;//一个客户端对应一个socket void ClientConnect(); void ReadData1(); };TcpServer.cpp #include "TcpServer.h" #include"qstring.h" #include"qdebug.h" #pragma execution_character_set("utf-8") TcpServer::TcpServer(QWidget *parent) : QWidget(parent) { ui.setupUi(this); server = new QTcpServer(this); //客户机连接信号槽 connect(server, &QTcpServer::newConnection, this, &TcpServer::ClientConnect); } TcpServer::~TcpServer() { server->close(); server->deleteLater(); } void TcpServer::on_btn_listen_clicked() { if (ui.btn_listen->text()=="侦听") { //从输入框获取端口号 int port = ui.lineEdit_port->text().toInt(); //侦听指定端口的所有ip if (!server->listen(QHostAddress::Any, port)) { //若出错,则输出错误信息 qDebug() << server->errorString(); return; } //修改按键文字 ui.btn_listen->setText("取消侦听"); } else { socket->abort(); //取消侦听 server->close(); //修改按键文字 ui.btn_listen->setText("侦听"); } } void TcpServer::ClientConnect() { //解析所有客户连接 while (server->hasPendingConnections()) { //连接上后通过socket获取连接信息 socket = server->nextPendingConnection(); QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort()); //提示连接成功 ui.textEdit_server->append(str+"Connect to the server"); //复选框选项为连接服务器的ip ui.comboBox->addItem(str); //将socket地址放入combobox属性内 //ui.comboBox->setItemData(ui.comboBox->count()-1, QVariant((int)socket)); //监听客户端是否有消息发送 connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1); } } //获取客户端向服务器发送的信息 void TcpServer::ReadData1() { QByteArray buf = socket->readAll();//readAll最多接收65532的数据 QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort()); ui.textEdit_server->append(str +QString(buf)); } //服务器向客户端发送信息 void TcpServer::on_btn_server_clicked() { if(ui.comboBox->count()== 0)return; //QTcpSocket* skt= (QTcpSocket*)ui.comboBox->itemData(ui.comboBox->currentIndex()).value(); socket->write(ui.lineEdit1->text().toUtf8()); }
注意 :write中需要写入char类型的元素或QByteArray类型的元素
效果展示:
三,TCP/IP文件传输
上文实现了消息的传输,由于 socket->readAll();(readAll最多接收65532的数据) ,因此对于大文件的传输用此方法是不可取的。
点击领取Qt学习资料+视频教程~「链接」
TCP/IP文件传输的思路: 客户端和服务器连接 客户端选择文件,并发送文件给服务器(发送的是文件的帧头,格式:文件名&大小) 服务器触发readyRead,然后解析文件帧头(获取文件名和大小),并返回客户端一个ok消息 客户端触发readyRead,然后发送文件数据,通过progressBar显示进度 服务器再次触发readyRead,接收文件数据,并保存(通过ishead判断接收的是文件帧头还是文件数据)
代码实现:
新建服务器项目(TcpServer) TcpServer.h #pragma once #include #include "ui_TcpServer.h" #include"qtcpserver.h" #include"qtcpsocket.h" #pragma execution_character_set("utf-8") class TcpServer : public QWidget { Q_OBJECT public: TcpServer(QWidget *parent = Q_NULLPTR); void hasConnect(); private: Ui::TcpServerClass ui; QTcpServer* server; QTcpSocket* socket; bool ishead; QString fileName; int fileSize;//接收文件的总大小 int recvSize;//当前接收文件的大小 QByteArray filebuf;//当前接收的文件数据 };TcpServer.cpp #include "TcpServer.h" #include"qfile.h" TcpServer::TcpServer(QWidget *parent) : QWidget(parent) { ishead = true; ui.setupUi(this); server = new QTcpServer(this); //监听1122端口的ip server->listen(QHostAddress::Any, 1122); //如果有用户连接触发槽函数 connect(server, &QTcpServer::newConnection, this, &TcpServer::hasConnect); } void TcpServer::hasConnect() { while (server->hasPendingConnections()>0)//判断当前连接了多少人 { //用socket和我们的客户端连接,一个客户端对应一个套接字socket socket = server->nextPendingConnection(); //服务器界面上输出客户端信息 ui.textEdit->append(QString("%1:新用户连接").arg(socket->peerPort())); //如果客户端发送信息过来了,触发匿名函数 connect(socket, &QTcpSocket::readyRead, [=]() { QByteArray buf = socket->readAll(); //用一个标志位ishead判断是头还是数据位 if (ishead) { //如果是头,解析头(文件名,文件大小) QString str = QString(buf); ui.textEdit->append(str); QStringList strlist = str.split("&"); fileName = strlist.at(0);//解析帧头文件名 fileSize = strlist.at(1).toInt();//解析帧头文件大小 ishead = false;//下次接收到的文件就是我们的数据 recvSize = 0; filebuf.clear(); socket->write("ok"); } else { //根据文件名和文件大小接收和保存文件 filebuf.append(buf); recvSize += buf.size();//每接收一次文件,当前文件大小+1 //当接收文件大小等于总文件大小,即文件数据接收完毕 if (recvSize>=fileSize) { //保存文件 QFile file(ui.lineEdit->text() + fileName); file.open(QIODevice::WriteOnly); file.write(filebuf); file.close(); ishead = true; } } }); } }
新建客户端项目(QTcpClient) QTcpClient.h #include #include"ui_QTcpClient.h" #include"qtcpsocket.h" #pragma execution_character_set("utf-8") class QTcpClient : public QWidget { Q_OBJECT public: QTcpClient(QWidget *parent = Q_NULLPTR); public slots: void on_btn_connect_clicked(); void on_btn_choose_clicked(); void on_btn_open_clicked(); private: Ui::QTcpClientClass ui; QTcpSocket* socket; };QTcpClient.cpp #include "QTcpClient.h" #include"qfiledialog.h" #include"qfileinfo.h" QTcpClient::QTcpClient(QWidget *parent) : QWidget(parent) { ui.setupUi(this); socket = new QTcpSocket(this); } void QTcpClient::on_btn_connect_clicked() { QString ip = ui.lineEdit_ip->text();//获取ip int port = ui.lineEdit_port->text().toInt();//获取端口数据 socket->connectToHost(ip, port);//连接服务器 //等待连接成功 if (socket->waitForConnected()) { ui.textEdit->append("连接服务器成功!"); ui.btn_open->setEnabled(true); //如果服务器发送信息到客户端,触发匿名函数 connect(socket, &QTcpSocket::readyRead, [=]() { //读取服务器发送的信息(即缓冲区信息) QByteArray buf = socket->readAll(); if (buf=="ok") { QFile file = (ui.label_path->text()); if (!file.open(QIODevice::ReadWrite)) { //读取文件失败 return; } qint64 currentlen = 0;//当前已经发送的大小 qint64 allLength = file.size();//总文件大小 do { char data[1024]; qint64 msize = file.read(data, 1024);//读文件放入打他数组中,返回读取到的大小 socket->write(data, msize);//把读取到的data数据发送给服务器 currentlen += msize;//实时获取当前发送的文件大小 ui.progressBar->setValue(currentlen *100 / allLength);//更新界面进度条 } while (currentlen < allLength);//当发送文件等于文件大小时,发送完毕,循环结束 } }); } else { ui.textEdit->append("连接服务器失败!"); } } //选择文件事件 void QTcpClient::on_btn_choose_clicked() { QString path = QFileDialog::getOpenFileName(this, "打开文件", "", "(*.*)"); ui.label_path->setText(path); } //发送文件事件 void QTcpClient::on_btn_open_clicked() { QFileInfo info(ui.label_path->text()); //用QFileInfo::fileName,size获取文件名和大小 格式:文件名&大小 //服务器用该格式解析文件名和大小 QString head = QString("%1&%2").arg(info.fileName()).arg(info.size()); //将该格式发送给服务器 toUtf8:QString转QByteArray或char类型 socket->write(head.toUtf8()); }
效果展示:
浮梁有数个柳溪村,这个五色缤纷的,你去过吗?地处鹅湖镇南部的柳溪村,有着天宝堂里一明珠美誉。这里山水环绕,风光旖旎,是诗人笔下的世外桃源这里文化底蕴深厚古迹众多,是历史悠久的人文古村这里产业兴旺朝气蓬勃,是活力四射的梦里乡村
注意本文将占用您4分钟的阅读时间但会回报您10分的精彩最近几天,北京又有一批景区景点暂时关闭或暂停服务,有的是进入了冬季闭园阶段,有的是因为检修消防疫情防控等原因临时关闭,今天就再整理
11月15日起,泰山碧霞祠等3处景区有序恢复开放央广网北京11月15日消息据泰山景区微信公众号消息,近日,泰山景区管委会按照上级有关要求,结合当前疫情防控形势,决定自11月15日起,碧霞祠王母池普照寺等3处场所有序恢复开放。近七
四川这一景区走红,被誉为亚洲第一漂,距巴中市约2小时车程在四川境内有这样一个景区,被称为亚洲第一漂,与巴中市的距离大约为车程两个小时,这座景区的历史也特别悠久。我们可以想一想,四川在古代时就已经存在了,而且这里的经济一直都特别发达,不然
喜讯!长沙县这2镇2村获省级荣誉,有你家乡吗?喜大普奔!喜大普奔!近日湖南省文化和旅游厅省发展和改革委员会公布了第四批湖南省乡村旅游重点村和第二批湖南省乡村旅游重点镇(乡)名单长沙县金井镇果园镇入选第二批湖南省乡村旅游重点镇(
不用千里迢迢去厦门,汕尾也有鼓浪屿啦小岛渔村,也叫屿仔岛一座被海四面包围的小岛岛上面积0。34平方公里环境优美空气清新需要坐船才能够到达想和你一起看海也想和海一起看你1hr最原始的小岛,汕尾鼓浪屿NANAO这里可以说
冬季国内这12个最佳赏雪地,每一个都让人着迷,快来看看吧冬天除了像韩剧里恋人们在大雪纷飞的雪地里被撒狗粮外,其实最大的乐趣应该是打雪仗堆雪人,在漫天飞舞的大雪里撒泼狂欢,那才是人生一大乐事。那么在中国,哪些地方适合冬季看雪呢?哪些地方适
延庆野鸭湖冬日新玩法,看白天鹅灰鹤水边嬉戏野鸭湖国家湿地公园位于延庆西部,湿地面积达3939公顷,是北京面积最大类型最多生物多样性最丰富的湿地自然保护区之一。初冬的野鸭湖很美,金黄铺地的银杏,斑驳灿烂的红叶,还有湖畔随风摇
我国已设立901处国家湿地公园总面积达360万公顷记者日前从湿地公约第十四届缔约方大会中国国家湿地公园的保护与发展分论坛上获悉,截至目前,中国国家湿地公园总数达901处,总面积达360万公顷。武汉华侨城生态湿地公园。新华社记者程敏
徒步考察中国革命史日记第220221222223225226天徒步考察中国革命史日记1987。8。11989。10。1徒步考察中国革命史日记第220221222223224225226天休息了几天,反而不适应了,脚走起来感到疼痛不已。当然,今
来三水马尔代夫,感受别样诗意阳光沙滩渔村美食在三水区白坭镇隐藏着一个观光胜地波子角,被当地人称为小马尔代夫。波子角位于西江边,驱车到白坭镇水运村,便可以找到它。在三水县地名志,可以找到关于波子角的图片记载傍晚
三国志11献帝捅刀既然曹操被杀得虎落平阳,就让他也做一回傀儡开头就把剧本来源说清楚,其实这是三国志11游戏的老骥伏枥章节,只不过演示的主视角不是曹操,而换成了汉献帝。假想曹操在官渡之战输了个底儿掉,只能退保许昌,献帝刘协也趁机在洛阳发动宫变
氧护人生高压氧的神奇保健功效,你真的了解吗?高压氧不仅对众多疾病有治疗效用,通过调节压力值,也可达到很好的保健效果。保健作用01hr消除疲劳提高工作效率人脑耗氧量占全身20,且对缺氧特别敏感。供氧不足,会引起体力不支头昏失眠
茶也分寒热?你喝对茶了吗?喝普洱茶的朋友都知道,普洱茶分为生普和熟普,有一种说法叫做生茶寒,熟茶热。根据我们的生活经验,很多食物都分寒热。一般来说,绿色的味苦的食物偏寒,而红色的食物偏热中医讲究将食物分为寒
漫画为了排出宿便,人类交了上千年的智商税排便虽然听起来很有味道,但是和人体健康息息相关。很多人觉得上厕所而已,每天一次特别顺利。对于有些人来说,拉粑粑都是非常痛苦的,比如便秘。他们几天拉不了一次,每次拉的时候都无比折磨。
痛风患者的心声有一次自己的脚趾头痛,脚都瘸了,以为扭伤,到医院挂了外科,外科医生给我拍了片,说不是扭伤,叫我挂风湿病科后,经过化验尿酸值600多,我就确诊了是风湿病的痛风,属于代谢疾病的一种。医
蔬菜怎么吃更健康?蔬菜焯拌吃最好。很多人做菜,习惯油脂多,盐分多,这样一来,蔬菜非但不能带来减少脂肪的效果反而有增加体重的危险,盐分过多则容易造成血压增高。还有些人喜欢吃炖菜,可是蔬菜中的维生素C和
南方人最爱的马蹄竟不能生吃?小心感染寄生虫!马蹄,学名荸荠很多广东人从小吃到大可是你知道吗?生吃马蹄有感染寄生虫的风险荸荠(bqi)是冬季的时令佳品。荸荠又称马蹄,俗称地栗。它皮薄肉嫩,爽脆可口,不仅含有钙磷铁胡萝卜素维生素
中医是怎样延缓衰老的?中医学的整体观念在养生防病预防衰老过程中起到了重要的作用,中医未病先防既病防变瘥后防复的治未病思想,很好地概括了抗衰老的核心原则。素问四气调神大论指出圣人不治已病治未病,夫病已成而
维生素的信息要点1维生素A作用与视觉有关,并能维持粘膜正常功能,调节皮肤状态。帮助人体生长和组织修补,对眼睛保健很重要,能抵御细菌以免感染,保护上皮组织健康,促进骨骼与牙齿发育。缺乏症夜盲症眼球干
湖人完败!新援全队最高,谁注意到浓眉哥赛后采访,还有希望!北京时间2月14日,NBA迎来一场焦点战,也是西部季后赛附加赛资格的一场卡位战湖人大开拓者。前者刚刚不敌雷霆排在西部第12位,后者则是险胜勇士位列西部第13位。比赛上来,湖人率先进
湖人115127负开拓者,可怕的不是对手的三分,而是自己的软弱今日NBA常规赛,洛杉矶湖人在又一场关键的西部季后赛卡位战中负于直接竞争对手波特兰开拓者,目前26胜32负,距离季后赛已经越来越远。本场比赛勒布朗詹姆斯依旧因为脚伤缺阵,但新援外除