socket(二)使用QT实现简单连接
首先,为了让我们完成更好的观察,我们需要先绘制主机与从机的界面:
我们之所以能够实现连接,主要是因为IP与端口为我们指引了他们的地址,所以我们才能精确的连接上。所以我们绘制这两个界面的时候,需要绘制输入IP与端口的输入框;由于我们需要通过socket去完成收发信息的工作,所以我们自然需要在这两个界面绘制接收区与输入区,绘制发送按钮;由于多个从机可以连接上主机,主机需要从多个从机中选择所需要的从机发送信息或者是向所有从机发送信息,所以我们需要一个下拉列表框,来选择我们需要发送信息的从机。
主机思路与代码
在QT中,有两个方便我们完成socket的头文件:
#include <QTcpServer> #include <QTcpSocket>
我们要使用他们,需要在.pro文件中添加
QT += core gui network
我们在界面中,对IP与端口的输入框进行转到槽操作,在函数中获取输入的IP与端口,端口为2-2^23次方,期间可以随意选择。
void MainWindow::on_le_address_textChanged(const QString &arg) { ip = arg; } void MainWindow::on_le_port_textChanged(const QString &arg) { port = arg.toInt(); }
当我们点击监听按钮,实现实现监听操作时,即创建一个新的TCP服务器对象,在指定的IP与端口上监听,如果监听成功,其IP与端口的输入框与其监听按钮都应失效,不可输入,不可点击,且设置一个信号槽去处理新链接。如果监听失败,则显示警告
void MainWindow::on_pb_listen_clicked() { tcpServer = new QTcpServer(); if(tcpServer->listen(QHostAddress(ip),port)) { ui->le_port->setEnabled(false); ui->le_address->setEnabled(false); ui->pb_listen->setEnabled(false); QObject::connect(tcpServer,SIGNAL(newConnection()),this,SLOT(addClient())); } else { QMessageBox::warning(this, "waring", "listen error", QMessageBox::Close); } }
当有新的客户端连接时被调用。首先从TCP服务器获取下一个待处理的连接,然后设置一个信号槽,当该连接有新数据可读时,将调用showMeassage()
函数。接着,它将新连接添加到list
列表中,并在一个下拉列表中添加一个新的项目来表示这个连接
void MainWindow::addClient() { QTcpSocket* tcpSocket = tcpServer->nextPendingConnection(); connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(showMeassage())); list.append(tcpSocket); ui->cb_clientList->addItem("tcpSocket" + QString::number(i)); i++; }
如果我们需要查看是哪一个客户端发来的信息,则需要通过sender()函数进行确认,然后从socket读取数据显示在文本框中,为了分辨是哪个客户端发送过来的信息,所以我们在发送信息的前面添加该客户端的IP与端口,由于发送数据结束标志为“\r\n”,且人为在输入区输入无效,所以我们要在代码底层逻辑添加“\r\n”
void MainWindow::showMeassage() { QTcpSocket* tcpSocket = (QTcpSocket*)sender(); QString ip = tcpSocket->peerAddress().toString(); qint16 port = tcpSocket->peerPort(); QString sender = ip + ":" + QString::number(port) + "-------->" + tcpSocket->readAll(); ui->tb_show->append(sender + "\r\n"); }
当主机需要发送信息时,如果需要向全体客户端发送信息,则选择广播,即下拉列表第一行,使用for循环向每一个客户端发送消息,如果只需要向其中一个客户端发送消息,则在下拉列表选择该项即可,当发送完消息后,清除发送区
void MainWindow::on_pb_send_clicked() { QString message = ui->te_send->toPlainText(); if(ui->cb_clientList->currentIndex() == 0) { for (int var = 0; var < list.size(); ++var) { QTcpSocket* tcpSocket = list[var]; tcpSocket->write(message.toUtf8()); } } else { QTcpSocket* tcpSocket = list[ui->cb_clientList->currentIndex() - 1]; tcpSocket->write(message.toUtf8()); } ui->te_send->clear(); }
从机的代码与思路
与主机一样,添加头文件与.pro模块
然后,从机需要连接主机,所以从机需要在IP与端口的输入框中输入主机的IP与端口,然后通过转到槽获取
void MainWindow::on_le_address_textChanged(const QString &arg) { ip = arg; } void MainWindow::on_le_port_textChanged(const QString &arg) { port = arg.toInt(); }
输入IP与端口后,点击链接,创建新QTcpSocket对象,建立新TCP链接,当有新数据可读时,使用showMeassage()函数显示数据。当链接成功时,使用信号槽调用函数禁用界面元素,当链接断开时,使用信号槽调用函数启用界面元素
void MainWindow::on_pb_connect_clicked() { tcpSocket = new QTcpSocket(); tcpSocket->connectToHost("127.0.0.1",8888); connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(showMeassage())); connect(tcpSocket,SIGNAL(connected()),this,SLOT(enableFalse())); connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(enableTrue())); }
我们根据传入的布尔值来判断启用还是禁用元素
void MainWindow::enableTrue() { enable(true); } void MainWindow::enableFalse() { enable(false); } void MainWindow::enable(bool flage) { ui->le_port->setEnabled(flage); ui->le_address->setEnabled(flage); ui->pb_connect->setEnabled(flage); }
接收从主机发送过来的函数时,为了我们方便阅读,使用“\r\n”完成换行操作
void MainWindow::showMeassage() { ui->tb_show->append(tcpSocket->readAll() + "\r\n"); }
当从机发送信息完成后,我们要清楚发送区
void MainWindow::on_pb_send_clicked() { QString message = ui->te_send->toPlainText(); tcpSocket->write(message.toUtf8()); ui->te_send->clear(); }
运行效果