一、前言
Qt框架提供了QTcpServe和QTcpSocket两种类实现TCP协议的相关连接,TCP协议不再过多赘述,以下主要讲述自机服务器和自机客户端的连接实现TCP协议通信的具体方式。
二、基本功能介绍
使用Qt网络模块之前需要在.pro文件种加入network
服务器端
绑定端口,并通过信号判断新连入客户机,同时输出相关内容,同时通过断连信号进行客户端的删除。
收信功能绑定读取信号槽函数,对所带数据进行读取显示。
发信功能找到对应的客户机地址和端口进行发信
客户机端
连接端口
收信功能绑定读取信号槽函数
发信功能直接将发送数据写入设备
三、具体原理和代码实现
在.pro文件中加入network
QT += core gui network
初始化并分配指针空间
void Widget::Init() { myClient = new QTcpSocket(this); }
服务器端实现
1.ui设计
搭建IP地址框和端口框Line Edit,信息接收框和发送框Tetx Edit,两个PushButton进行绑定和发送
2.IP绑定
通过listen方法指示服务器侦听地址、地址和端口端口上的传入连接。如果端口为 0,则自动选择端口。如果 address 为 QHostAddress::Any,则服务器将侦听所有网络接口。
setEnable设置按钮是否可以使用
setMaxPendingConnection设置最大挂起客户机数量
void Widget::on_btn_bind_clicked() { QString myAddr; //手动设置ip myAddr = ui->ledit_serv_addr->text(); QString myPort = ui->ledit_serv_port->text(); QString msg; bool ret = myServer->listen(QHostAddress(myAddr),myPort.toUInt()); if(!ret) { msg = "绑定失败"; } else { msg = "绑定成功"; ui->btn_bind->setEnabled(false); } ui->textEdit->append(msg); myServer->setMaxPendingConnections(MAXNUM); connect(myServer,SIGNAL(newConnection()),this,SLOT(doProcessNewConnection())); connect(myServer,SIGNAL(acceptError(QAbstractSocket::SocketError)), this,SLOT(doProcessAcceptError(QAbstractSocket::SocketError))); }
3.客户端连入
nextPendingConnection(),用于等待并接受下一个挂起的连接。当一个客户端请求连接时,服务器会将该连接挂起,等待服务器接受连接。nextPendingConnection()函数会阻塞当前线程,直到有一个连接被接受。一旦有连接被接受,会返回一个QTcpSocket对象。
此时对客户端的IP地址和端口号进行读取并显示
void Widget::doProcessNewConnection() { client = myServer->nextPendingConnection(); arrayClient.append(client); QString msg = QString("客户端[%1:%2] 连入!") .arg(client->peerAddress().toString()) .arg(client->peerPort()); ui->textEdit->append(msg); //客户端断开 connect(client,SIGNAL(diconnected()),this,SLOT(doProcessDiconnected())); //读取内容 connect(client,SIGNAL(readyRead()),this,SLOT(doProcessReadyRead())); }
4.客户端退出
当客户端退出时,通过sender返回指向发送信号的对象的指针,同时遍历客户机列表删除对应的客户端
void Widget::doProcessDiconnected() { QTcpSocket *client = (QTcpSocket *)this->sender(); QString msg = QString("客户端[%1:%2] 退出!") .arg(client->peerAddress().toString()) .arg(client->peerPort()); ui->textEdit->append(msg); //删除对应客户端 for(int i= 0;i<arrayClient.length();i++) { if(arrayClient.at(i)->peerAddress() == client->peerAddress()) { if(arrayClient.at(i)->peerPort() == client->peerPort()) { arrayClient.removeAt(i); break; } } } }
5.接收信息
使用readyRead()信号,每当有新数据可用于从器件的当前读取通道读取时,该信号就会发出一次。将该信号内容进行循环读取并进行显示。
void Widget::doProcessReadyRead() { QTcpSocket *client = (QTcpSocket *)this->sender(); QString str1 = QString(QString("客户端[%1:%2] 说:") .arg(client->peerAddress().toString()) .arg(client->peerPort())); QString msg; QString str2; while (!client->atEnd()) { msg.append(QString(client->readAll())); } str2 = QString("%1%2").arg(str1).arg(msg); ui->textEdit->append(str2); }
6.发送信息
读取输入的客户端IP和端口信息,遍历客户端列表,找到对应的客户端通过write进行数据写入。
void Widget::on_btn_send_clicked() { QString ip = ui->ledit_client_ip->text(); QString port = ui->ledit_client_port->text(); //查找对应的客户 for(int i=0;i<arrayClient.length();i++) { if(arrayClient.at(i)->peerAddress().toString()==ip) { if(arrayClient.at(i)->peerPort() == port.toUInt()) { QString msg = ui->textEdit_2->toPlainText();\ arrayClient.at(i)->write(msg.toUtf8()); ui->textEdit_2->clear(); break; } } } }
客户机端实现
1.ui设计
搭建IP地址框和端口框Line Edit,信息接收框和发送框Tetx Edit,两个PushButton进行连接和发送
2.服务器连接
connectTohost对指定的IP地址和端口号进行连接,同时绑定四信号和槽函数
void Widget::on_btn_bind_clicked() { QString servIp = ui->ledit_serv_addr->text(); QString servPort = ui->ledit_serv_port->text(); myClient->connectToHost(QHostAddress(servIp),servPort.toUInt()); connect(myClient,SIGNAL(connected()),this,SLOT(doProcessConnected())); connect(myClient,SIGNAL(readyRead()),this,SLOT(dorProcessReadyRead())); connect(myClient,SIGNAL(disconnected()),this,SLOT(doProcessDisconnected())); connect(myClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(doProcessError(QAbstractSocket::SocketError))); }
3.信息接收
readyRead信号进行读取,与服务器端相同
void Widget::dorProcessReadyRead() { QString msg,str1,str2; str1 = QString(" 服务器[%1:%2] 说:") .arg(myClient->peerAddress().toString()) .arg(myClient->peerPort()); while(!myClient->atEnd()) { str2.append(QString(myClient->readAll())); } msg = QString("%1%2").arg(str1).arg(str2); ui->textEdit->append(msg); }
4.信息发送
write进行写入,与服务器端相同
void Widget::on_btn_send_clicked() { QString msg = ui->textEdit_2->toPlainText(); int ret = myClient->write(msg.toUtf8()); if(ret<=0) return; ui->textEdit_2->clear(); }
四、关于端口号和地址
windows操作系统下,可以通过cmd进行IP地址和端口号的查询
IP地址查询:ipconfig
一般使用IPV4右侧的IP地址
端口号查询:netstat -na
TCP对应的端口号,其中LISTENING代表监听状态,ESTABLISHED代表已连接,TIME_WAIT代表结束访问,CLOSE_WAIT代表等待中断。