一.环境配置
系统:win11(win10运行需注意运行库更新到新版本)
Qt版本:Qt.6.3以上
ip和端口:IP可以自动获取,端口这里设置为1201,注意项目部署的时候服务器端需要配置(windows防火墙高级设置,新建输出端口1201,也可以自定)
二.前言
本实验是实现tcp通讯功能,客户端连接上服务器端后,可以与服务器端互发消息
三.所需库函数介绍
1.QT += network
实现qt网络功能的模块,不过多赘述
2.#include<QTcpServer>
公共函数:
信号:
3.#include<QTcpSocket>
成员函数:
四.正文
1.UI界面预设:
2.主函数
初始化预设的指针以及状态栏label,并且获取服务器的地址更新comboip
ui->setupUi(this);
tcpserver=new QTcpServer(this);
//lablisten初始化并添加到状态栏上面
labListen =new QLabel("监听状态:");
labListen->setMinimumWidth(150);
ui->statusBar->addWidget(labListen);
//labsocket初始化并添加到状态栏上面
labSocketState= new QLabel("Socket状态:");
labSocketState->setMinimumWidth(150);
ui->statusBar->addWidget(labSocketState);
//获取服务器的信息,包括名字IP等,并添加在comboIP中
QString name=QHostInfo::localHostName();
QHostInfo hostinfo=QHostInfo::fromName(name);
QString localIp ;
foreach (const auto &item, hostinfo.addresses()) {
if(item.protocol()==QAbstractSocket::IPv4Protocol){
localIp=item.toString();
ui->comboIP->addItem(localIp);
}
}
connect(tcpserver,&QTcpServer::newConnection,this,&MainWindow::do_newConnection);//每次有新连接可用的时候都会发送newConnection信号
}
其中connect是处理获取到新连接信号的时候,触发do_newConnection槽函数:
void MainWindow::do_newConnection()
{
tcpsocket=tcpserver->nextPendingConnection();//将tcpsocket指向被监听到的端口
connect(tcpsocket,&QTcpSocket::connected,this,&MainWindow::do_connected);
connect(tcpsocket,&QTcpSocket::disconnected,this,&MainWindow::do_disconnected);
connect(tcpsocket,&QTcpSocket::readyRead,this,&MainWindow::do_readyRead);
connect(tcpsocket,&QTcpSocket::stateChanged,this,&MainWindow::do_stateChanged);
// 手动触发 stateChanged 信号
emit tcpsocket->stateChanged(tcpsocket->state());
}
&QTcpSocket::connected:连接信号触发
&QTcpSocket::disconnected:断开连接信号触发
&QTcpSocket::readyRead:当读取缓冲区收到数据流时触发该信号
&QTcpSocket::stateChanged:当socket状态改变时候触发该信号
emit tcpsocket->stateChanged(tcpsocket->state());:手动触发 stateChanged 信号,不手动触发的话有客户端连接上状态栏socket状态标签会没变化
上面四个信号触发时调用的对应的槽函数:
void MainWindow::do_connected()
{
ui->textEdit->appendPlainText("**client socket connected**");
ui->textEdit->appendPlainText("peer name:"+tcpsocket->peerName());
ui->textEdit->appendPlainText("peer address:"+tcpsocket->peerAddress().toString());
ui->textEdit->appendPlainText("peer port:"+QString::number(tcpsocket->peerPort()));
}
void MainWindow::do_disconnected()
{
ui->textEdit->appendPlainText("**client socket disconnected**");
tcpsocket->deleteLater();//并不是马上删除,而是等待一些事务处理完后才删除socket
tcpsocket = nullptr; // 设置为 nullptr,避免悬空指针
}
void MainWindow::do_readyRead()//当有数据传过来的时候,我们要消费这些数据
{
while (tcpsocket->canReadLine()) {
ui->textEdit->appendPlainText("[in] "+tcpsocket->readLine());
}
}
void MainWindow::do_stateChanged(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
labSocketState->setText("socket状态:UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
labSocketState->setText("socket状态:HostLookupState");
break;
case QAbstractSocket::ConnectingState:
labSocketState->setText("socket状态:ConnectingState");
break;
case QAbstractSocket::ConnectedState:
labSocketState->setText("socket状态:ConnectedState");
break;
case QAbstractSocket::BoundState:
labSocketState->setText("socket状态:BoundState");
break;
case QAbstractSocket::ListeningState:
labSocketState->setText("socket状态:ListeningState");
break;
case QAbstractSocket::ClosingState:
labSocketState->setText("socket状态:ClosingState");
break;
}
}
其中SocketState是枚举类:
3.开始监听功能
获取ui界面上面的IP和端口号,调用tcpserver->listen开始监听,第一个参数是ip地址,第二个的参数是端口号
//开始监听
void MainWindow::on_actStart_triggered()
{
QString IP=ui->comboIP->currentText();
quint16 port=ui->spinPort->value();
QHostAddress hostaddress(IP);
tcpserver->listen(hostaddress,port);
ui->textEdit->appendPlainText("**开始监听**");
ui->textEdit->appendPlainText("服务器地址:"+tcpserver->serverAddress().toString());
ui->textEdit->appendPlainText("服务器端口号:"+QString::number(tcpserver->serverPort()));
ui->actStart->setEnabled(false);
ui->actStop->setEnabled(true);
labListen->setText("监听状态:正在监听...");
}
4.停止监听
当tcpsocket不是空指针并且socket是连接状态时,调用disconnectFromHost()断开socket连接
//停止监听
void MainWindow::on_actStop_triggered()
{
if(tcpsocket!=nullptr&&tcpsocket->state()==QAbstractSocket::ConnectedState){
tcpsocket->disconnectFromHost();
}
//设置act状态
ui->actStart->setEnabled(true);
ui->actStop->setEnabled(false);
//停止监听
if(tcpserver->isListening()){
tcpserver->close();
labListen->setText("监听状态:监听已停止");
}
}
5.发送消息
//发送消息
void MainWindow::on_btnSend_clicked()
{
//如果消息框为空,不发送
bool x =ui->editMsg->text().isEmpty();
if(x){
ui->textEdit->appendPlainText("消息框为空");
return;}
//如果没有链接,不发送
if(tcpsocket==nullptr){
ui->textEdit->appendPlainText("链接没有建立");
return;}
//将消息框内容通过socket发送出去,并在聊天框中显示出来
QString message=ui->editMsg->text();
ui->textEdit->appendPlainText("[out]: "+message);
QByteArray ms=message.toUtf8();
ms.append("\n");
tcpsocket->write(ms);
ui->editMsg->clear();
}
因为数据的读取用的是readline,是一行一行读取,以换行符作为一串数据结束识别的符号,所以如果要用readline注意要在数据后面加上\n