49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)
在上章 48.qt-网络通信讲解1,我们学习了网络通信基础后,本章便来实战一篇.源码正在整理中,等下贴地址.
ps:支持客户端和服务器,提供源码,并且服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.
1.效果图所下所示:
如下图所示,当服务器状态下,如果有客户端连入,会提示客户端信息:
2.效果操作
客户端操作:
服务器操作:
从上面操作可以看出,服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.
3.首先创建ui
4.注意事项
不管是服务器还是客户端,都可以通过peeraddress()和peerport()来获取目标地址和目标端口
4.1服务器监听时
比如服务器,则可以通过qtcpsocket的peeraddress()则可以获取连入的客户端地址
也可以通过children()来获取所有连入的客户端(需要注意的是也会获取到服务器本身的tcp地址和端口),示例如下:
qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>(); foreach (qtcpsocket *tcp, m_tcps) { qdebug() << "address:" << tcp->peeraddress (); qdebug() << "port:" << tcp->peerport (); }
如果我们只向连入的客户端某个端口发送数据时,就可以通过上面的方式筛选出来.
这样做的话如果觉得很麻烦,也可以将之前连接上的客户端存到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 头文件介绍
#ifndef widget_h #define widget_h #include <qwidget> #include <qtcpsocket> #include <qtcpserver> #include <qmessagebox> namespace ui { class widget; } class widget : public qwidget { q_object qtcpsocket m_client; qtcpserver m_server; qstring targetaddr; int targetport; public: explicit widget(qwidget *parent = 0); ~widget(); private slots: void on_btn_switch_clicked(); void on_tcpmode_currentindexchanged(int index); void on_btn_send_clicked(); //发送按钮 void on_betn_clear_clicked(); //清空按钮 //客户端槽函数 void onclientconnected(); void onclientdisconnected(); void onclientdataready(); void onclientbyteswritten(qint64 bytes); void onclienterr(qabstractsocket::socketerror socketerror); //服务器槽函数 void onservernewconnection(); void onserverconnected(); void onserverdisconnected(); void onserverdataready(); void onserverbyteswritten(qint64 bytes); private: void startconnect(bool ison); void initclientsignals(); //初始化客户端信号槽 bool startclient(); //启动客户端 void initserversignals(); //初始化客户端信号槽 bool startserver(); //启动服务器 ui::widget *ui; }; #endif // widget_h
5.2 widget.cpp介绍
该cpp主要是用来处理界面操作的函数
#include "widget.h" #include "ui_widget.h" widget::widget(qwidget *parent) : qwidget(parent), ui(new ui::widget) { ui->setupui(this); startconnect(false); on_tcpmode_currentindexchanged(0); initclientsignals(); //初始化客户端信号槽 initserversignals(); //初始化服务器信号槽 //限制只能数字输入 qregexp regx("[0-9]+$"); qvalidator *validator = new qregexpvalidator(regx, this ); ui->ipaddr1->setvalidator( validator ); ui->ipaddr2->setvalidator( validator ); ui->ipaddr3->setvalidator( validator ); ui->ipaddr4->setvalidator( validator ); ui->ipport->setvalidator( validator ); } void widget::on_tcpmode_currentindexchanged(int index) { if(index==0) //clent { ui->ipportlabel->settext("服务器端口号:"); ui->ipaddlabel->show(); ui->ipadds->show(); ui->targetlabel->hide(); ui->targetobject->hide(); } else { ui->ipaddlabel->hide(); ui->ipadds->hide(); ui->ipportlabel->settext("本地端口号:"); } } void widget::on_btn_switch_clicked() //切换连接开关 { bool ret; if(ui->btn_switch->text()=="打开连接") { if(ui->tcpmode->currentindex()==0) //启动客户端 ret=startclient() ; else ret=startserver(); if(ret) startconnect(true); } else { if(ui->tcpmode->currentindex()==0) //启动客户端 m_client.close(); else { if( m_server.islistening() ) { qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>(); foreach (qtcpsocket *tcp, m_tcps) { tcp->close(); } m_server.close(); } } startconnect(false); } } void widget::startconnect(bool ison) { if(!ison) { ui->btn_switch->setstylesheet("color:blue;border: 1px solid blue"); ui->btn_switch->settext("打开连接"); //使能 ui->ipaddr1->setenabled(true); ui->ipaddr2->setenabled(true); ui->ipaddr3->setenabled(true); ui->ipaddr4->setenabled(true); ui->tcpmode->setenabled(true); ui->ipport->setenabled(true); ui->localport->settext(""); } else { ui->btn_switch->setstylesheet("color:red;border: 1px solid red"); ui->btn_switch->settext("关闭连接"); //失能 ui->ipaddr1->setenabled(false); ui->ipaddr2->setenabled(false); ui->ipaddr3->setenabled(false); ui->ipaddr4->setenabled(false); ui->tcpmode->setenabled(false); ui->ipport->setenabled(false); targetaddr=""; targetport=0; ui->sendlenlabel->settext("0"); } } void widget::on_betn_clear_clicked() { ui->recvedit->clear(); targetaddr=""; targetport=0; } void widget::on_btn_send_clicked() { if(ui->btn_switch->text()!="打开连接") { if(ui->tcpmode->currentindex()==0) //客户端 { m_client.write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length()); } else { if(ui->targetobject->currenttext()!="所有对象") { qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>(); foreach (qtcpsocket *tcp, m_tcps) { if(ui->targetobject->currenttext() == tcp->objectname()) { tcp->write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length()); break; } } } else //所有连接上的客户端都发送一遍 { qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>(); foreach (qtcpsocket *tcp, m_tcps) { tcp->write(ui->sendedit->toplaintext().tolocal8bit(),ui->sendedit->toplaintext().tolocal8bit().length()); } } } } } widget::~widget() { qlist<qtcpsocket *> m_tcps = m_server.findchildren<qtcpsocket *>(); foreach (qtcpsocket *tcp, m_tcps) { tcp->close(); } if(m_client.isopen()) { m_client.close(); qdebug()<<"m_client close"; } delete ui; }
5.3 clenthandler.cpp介绍
该cpp主要用来处理客户端操作相关的文件.
#include "widget.h" #include "ui_widget.h" void widget::initclientsignals() //初始化客户端信号槽 { connect(&m_client, signal(connected()), this, slot(onclientconnected())); connect(&m_client, signal(disconnected()), this, slot(onclientdisconnected())); connect(&m_client, signal(readyread()), this, slot(onclientdataready())); connect(&m_client, signal(byteswritten(qint64)), this, slot(onclientbyteswritten(qint64))); connect(&m_client, signal(error(qabstractsocket::socketerror )), this, slot(onclienterr(qabstractsocket::socketerror))); } bool widget::startclient() //启动客户端 { qstring ip = qstring("%1.%2.%3.%4").arg(ui->ipaddr1->text()).arg(ui->ipaddr2->text()).arg(ui->ipaddr3->text()).arg(ui->ipaddr4->text()); qdebug()<<ip; m_client.connecttohost(ip, ui->ipport->text().toint()); if(m_client.waitforconnected(800)) { return true; } else { qmessagebox::information(this,"提示",qstring("连接超时"),qmessagebox::ok); return false; } } void widget::onclientconnected() { startconnect(true); qmessagebox::information(this,"提示","连接成功",qmessagebox::ok); ui->localport->settext(qstring("%1").arg(m_client.localport())); //显示本地端口号 } void widget::onclientdisconnected() { startconnect(false); qmessagebox::information(this,"提示","断开完成",qmessagebox::ok); } void widget::onclientdataready() { if(m_client.peeraddress().tostring()!=targetaddr || m_client.peerport()!=targetport ) { targetaddr = m_client.peeraddress().tostring(); targetport = m_client.peerport(); ui->recvedit->insertplaintext("[接受来自"+ targetaddr+":"+qstring("%1").arg(targetport)+"]:\r\n"); } ui->recvedit->movecursor(qtextcursor::end); ui->recvedit->insertplaintext(qstring::fromlocal8bit(m_client.readall())+"\r\n"); } void widget::onclientbyteswritten(qint64 bytes) { qdebug() << "onbyteswritten:" << bytes; ui->sendlenlabel->settext(qstring("%1").arg(ui->sendlenlabel->text().toint()+bytes)); } void widget::onclienterr(qabstractsocket::socketerror socketerror) { qdebug()<<"onclienterr:"<<socketerror; m_client.close(); startconnect(false); qmessagebox::information(this,"提示",qstring("连接失败:%1").arg((int)socketerror),qmessagebox::ok); }
5.4 serverhandler.cpp介绍
该cpp主要用来处理服务器操作相关的文件
#include "widget.h" #include "ui_widget.h" void widget::initserversignals() //初始化信号槽 { connect(&m_server, signal(newconnection()), this, slot(onservernewconnection())); } bool widget::startserver() //启动服务器 { if(m_server.listen(qhostaddress::anyipv4,ui->ipport->text().toint())) //只监听ipv4的所有客户端 { ui->targetlabel->show(); ui->targetobject->show(); ui->localport->settext(qstring("%1").arg(m_server.serverport())); return true; } else return false; } void widget::onservernewconnection() { qdebug() << "onnewconnection"; qtcpsocket* tcp = m_server.nextpendingconnection(); //获取新的客户端信息 qstring info=tcp->peeraddress().tostring()+":"+qstring("%1").arg(tcp->peerport()); ui->targetobject->additem(info); qmessagebox::information(this,"提示",qstring("新的客户端连入:%1").arg(info),qmessagebox::ok); tcp->setobjectname(info); //设置名称,方便查找 connect(tcp, signal(connected()), this, slot(onserverconnected())); connect(tcp, signal(disconnected()), this, slot(onserverdisconnected())); connect(tcp, signal(readyread()), this, slot(onserverdataready())); connect(tcp, signal(byteswritten(qint64)), this, slot(onserverbyteswritten(qint64))); } void widget::onserverconnected() { } void widget::onserverdisconnected() { qtcpsocket* tcp = dynamic_cast<qtcpsocket*>(sender()); if( tcp != null ) //从连接对象中移除掉 { qdebug() << "onserverdisconnected"; qdebug() << "local address:" << tcp->peeraddress(); qdebug() << "local port:" << tcp->peerport(); qstring info=tcp->peeraddress().tostring()+":"+qstring("%1").arg(tcp->peerport()); qmessagebox::information(this,"提示",qstring("客户端断开连接:%1").arg(info),qmessagebox::ok); int index = ui-> targetobject ->findtext(info); if(index>=0) ui->targetobject->removeitem(index); } } void widget::onserverdataready() { qtcpsocket* tcp = dynamic_cast<qtcpsocket*>(sender()); if(tcp->peeraddress().tostring()!=targetaddr || tcp->peerport()!=targetport ) { targetaddr = tcp->peeraddress().tostring(); targetport = tcp->peerport(); ui->recvedit->insertplaintext("[接受来自"+ targetaddr+":"+qstring("%1").arg(targetport)+"]:\r\n"); } ui->recvedit->movecursor(qtextcursor::end); ui->recvedit->insertplaintext(qstring::fromlocal8bit(tcp->readall())+"\r\n"); } void widget::onserverbyteswritten(qint64 bytes) { qdebug() << "onbyteswritten:" << bytes; ui->sendlenlabel->settext(qstring("%1").arg(ui->sendlenlabel->text().toint()+bytes)); }
下一篇: 茶树菇炖鸽子的功效与做法