网站首页 > 技术文章 正文
作为演示TCP通信的实例,创建了一个TCPClient程序和一个TCPServer程序。
本文首先介绍TCPServer程序,运行时界面如图所示。
TCPServer程序具有如下的功能:
- 根据指定IP地址(本机地址)和端口打开网络监听,有客户端连接时创建socket连接;
- 采用基于行的数据通信协议,可以接收客户端发来的消息,也可以向客户端发送消息;
- 在状态栏显示服务器监听状态和socket的状态。
1. 主窗口定义与构造函数
TCPServer是一个窗口基于QMainWindow的应用程序,界面由UI设计器设计,MainWindow类的定义如下(忽略了 UI设计器自动生成的actions和按钮的槽函数):
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void closeEvent(QCloseEvent *event);
private:
Ui::MainWindow *ui;
QLabel *LabListen;//状态栏标签
QLabel *LabSocketState;//状态栏标签
QTcpServer *tcpServer; //TCP 服务器
QTcpSocket *tcpSocket;//TCP 通信的 Socket
QString getLocalIP();//获取本机 IP 地址
private slots:
//自定义槽函数
void onNewConnection();//QTcpServer的newConnection ()信号
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onClientConnected(); //Client Socket connected
void onClientDisconnected();//Client Socket disconnected
void onSocketReadyRead();//读取 socket 传入的数据
};
MainWindow中定义了私有变量tcpServer用于建立TCP服务器,定义了 tcpSocket用于与客户端进行socket连接和通信。
定义了几个槽函数,用于与QTcpServer和QTcpSocket的相关信号连接,实现相应的处理。
MainWindow构造函数代码如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
LabListen=new QLabel("监听状态:");
LabListen->setMinimumWidth(150);
ui->statusbar->addWidget(LabListen);
LabSocketState=new QLabel ("Socket 状态:");
LabSocketState->setMinimumWidth(200);
ui->statusbar->addWidget(LabSocketState);
QString localIP=getLocalIP(); //本机 IP
this->setWindowTitle(this->windowTitle()+" 本机IP: "+localIP);
ui->comboIP->addItem(localIP);
tcpServer=new QTcpServer(this);
connect(tcpServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));
}
QString MainWindow::getLocalIP()
{//获取本机IPv4地址
QString hostName=QHostInfo::localHostName(); //本地主机名
QHostInfo hostInfo=QHostInfo::fromName(hostName);
QString localIP="";
QList<QHostAddress> addList=hostInfo.addresses();
if(!addList.isEmpty()){
for(int i=0;i<addList.count();i++){
QHostAddress aHost=addList.at(i);
if(QAbstractSocket::IPv4Protocol==aHost.protocol()){
localIP=aHost.toString();
break;
}
}
return localIP;
}
}
MainWindow的构造函数创建状态栏上的标签用于信息显示,调用自定义函数getLocalIP()获取本机IP地址,并显示到标题栏上。创建QTcpServer实例tcpServer,并将其newConnection()信号与onNewConnection()槽函数关联。
2.网络监听与socket连接的建立
作为TCP服务器,QTcpServer类需要调用listen()在本机某个IP地址和端口上开始TCP监听,以等待TCP客户端的接入。单击主窗口上“开始监听”按钮可以开始网络监听,其代码如下:
void MainWindow::on_actionStart_triggered()
{//开始监听
QString IP=ui->comboIP->currentText();//IP地址
quint16 port=ui->spinPort->value();//端口
QHostAddress addr(IP);
tcpServer->listen(addr, port); //开始监听
ui->plainTextEdit->appendPlainText("**开始监听...");
ui->plainTextEdit->appendPlainText("**服务器地址:"+tcpServer->serverAddress().toString());
ui->plainTextEdit->appendPlainText ("服务器端口:"+QString::number(tcpServer->serverPort()));
ui->actionStart->setEnabled(false);
ui->actionStop->setEnabled(true);
LabListen->setText("监听状态:正在监听");
}
程序读取窗口上设置的监听地址和监听端口,然后调用QTcpServer的listen()函数开始监听。 TCP服务器在本机上监听,所以IP地址可以是表示本机的“127.0.0.1”,或是本机的实际IP,亦或是常量QHostAddress::LocalHost,即在本机上监听某个端口也可以写成:
tcpServer->listen(QHostAddress::LocalHost,port);
tcpServer开始监听后,TCPClient就可以通过IP地址和端口连接到此服务器。当有客户端接 入时,tcpServer会发射newConnection()信号,此信号关联的槽函数onNewConnection()的代码如下:
void MainWindow::onNewConnection()
{
tcpSocket = tcpServer->nextPendingConnection() ; //获取socket
connect(tcpSocket, SIGNAL(connected()),
this, SLOT(onClientConnected()));
onClientConnected();
connect(tcpSocket, SIGNAL(disconnected()),
this, SLOT(onClientDisconnected()));
connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(tcpSocket->state());
connect(tcpSocket,SIGNAL(readyRead()),
this,SLOT(onSocketReadyRead()));
}
程序首先通过nextPendingConnection()函数获取与接入连接进行通信的QTcpSocket对象实例tcpSocket,然后将tcpSocket的几个信号与相应的槽函数连接起来。
QTcpSocket的这几个信号的作用是:
- connected()信号,客户端socket连接建立时发射此信号;
- disconnected()信号,客户端socket连接断开时发射此信号;
- stateChanged(),本程序的socket状态变化时发射此信号;
- readyRead(),本程序的socket的读取缓冲区有新数据时发射此信号。
涉及状态变化的几个信号的槽函数代码如下:
void MainWindow::onClientConnected()
{//客户端接入时
ui->plainTextEdit->appendPlainText("**client socket connected");
ui->plainTextEdit->appendPlainText("**peer address:"
+tcpSocket->peerAddress().toString());
ui->plainTextEdit->appendPlainText("**peer port: "
+QString::number(tcpSocket->peerPort()));
}
void MainWindow::onClientDisconnected()
{//客户端断开连接时
ui->plainTextEdit->appendPlainText("**client socket disconnected");
tcpSocket->deleteLater();
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{//socket状态变化时
switch(socketState){
case QAbstractSocket::UnconnectedState:
LabSocketState->setText("socket 状态:UnconnectedState"); break;
case QAbstractSocket::HostLookupState:
LabSocketState->setText("scoket状态:HostLookupState"); break;
case QAbstractSocket::ConnectingState:
LabSocketState->setText("scoket状态:ConnectingState"); break;
case QAbstractSocket::ConnectedState:
LabSocketState->setText("scoket状态:ConnectedState"); break;
case QAbstractSocket::BoundState:
LabSocketState->setText("scoket状态:BoundState"); break;
case QAbstractSocket::ClosingState:
LabSocketState->setText("scoket ClosingState"); break;
case QAbstractSocket::ListeningState:
LabSocketState->setText("scoket 状态:ListeningState");
}
}
TCP服务器停止监听,只需调用QTcpServer的close()函数即可。窗口上的“停止监听”响应代码如下:
void MainWindow::on_actionStop_triggered()
{//停止监听
if(tcpServer->isListening()) //tcpServer 正在监听
tcpServer->close(); //停止监听
ui->actionStart->setEnabled(true);
ui->actionStop->setEnabled(false);
LabListen->setText("监听状态:己停止监听");
}
3.与TCPClient的数据通信
TCP服务器端和客户端之间通过QTcpSocket通信时,需要规定两者之间的通信协议,即传输的数据内容如何解析。QTcpSocket间接继承于QIODevice,所以支持流读写功能。
Socket之间的数据通信协议一般有两种方式,基于行的或基于数据块的。
基于行的数据通信协议一般用于纯文本数据的通信,每一行数据以一个换行符结束。 canReadLine()函数判断是否有新的一行数据需要读取,再用readLine()函数读取一行数据,例如:
while(tcpClient->canReadLine())
{
ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
}
基于块的数据通信协议用于一般的二进制数据的传输,需要自定义具体的格式。
实例程序TCPServer和TCPClient只是进行字符串的信息传输,类似于一个简单的聊天程序, 程序采用基于行的数据通信协议。
单击窗口上的“发送消息”,将文本框里的字符串发送给客户端,其实现代码如下:
void MainWindow::on_btnSend_clicked()
{ //发送一行字符串,以换行符结束
QString msg=ui->edtMsg->text();
ui->plainTextEdit->appendPlainText("[out] "+msg);
ui->edtMsg->clear();
ui->edtMsg->setFocus();
QByteArray str=msg.toUtf8();
str.append('\n');//添加一个换行符
tcpSocket->write(str);
}
从上面的代码中可以看到,读取文本框中的字符串到msg后,先将其转换为QByteArray类型字节数组str,然后在str最后面添加一个换行符,用QIODevice的write()函数写入缓冲区,这样就向客户端发送一行文字。
QTcpSocket接收到数据后,会发射readyRead()信号,在onNewConnection()槽函数中己经建 立了这个信号与槽函数onSocketReadyRead()的连接。
槽函数onSocketReadyRead()实现缓冲区数据的读取,其代码如下:
void MainWindow::onSocketReadyRead()
{ //读取缓冲区行文本
while(tcpSocket->canReadLine())
ui->plainTextEdit->appendPlainText("[in] "+tcpSocket->readLine());
}
这样,TCPServer就可以与TCPClient之间进行双向通信了,且这个连接将一直存在,直到某一方的QTcpSocket对象调用disconnectFromHost()函数断开socket连接。
————————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!
猜你喜欢
- 2024-10-24 QT(17)- QNetworkAccessManager qnetworkinterface.allinterfaces
- 2024-10-24 Qt多线程的三种方法QThread qt多线程直接处理数据
- 2024-10-24 Qt Core学习日记——第九天QObjectData
- 2024-10-24 Qt智能指针--QSharedPointer qt智能指针.get和.data函数后计数会加吗
- 2024-10-24 QT(11)- QThread qt ui thread work thread
- 2024-10-24 Qt QVariant的用法 qt基本语法
- 2024-10-24 Qt5中QOverload的用法 qt5coredll
- 2024-10-24 Qt多线程编程之QThread qt中的多线程
- 2024-10-24 Qt QModbusReply类 qty是什么单位的缩写
- 2024-10-24 Qt Core学习日记——第十一天QObject(下)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)