Qt的TCP通信—客户端的实现
客户端程序TCPClient只需要使用一个QTcpSocket对象,就可以和服务端程序TCPServer进行通信
服务端的实现流程
客户端的QTcpSocket首先通过connectToHost()尝试连接到服务器,需要指定服务器的IP地址和端口,connectToHost()是异步方式连接到服务器,不会阻塞程序运行,连接后发射connected()信号。
与服务器端建立socket连接后,就可以向缓冲区写数据或从接收缓冲区读取数据,实现数据的通信。当缓冲区有数据进入时,回发射readyRead()信号,一般在此信号的槽函数里读取缓冲区数据。
QTcpSocket是从QIODevice间接继承的,所以可以使用流数据读写功能。一个QTcpSocket既可以接收数据也可以发送数据,而且接收与发送是异步工作的,有各自的缓冲区。
客户端程序运行界面如图所示
实现功能:
1、通过IP地址和端口号连接到服务器
2、采用基于行的数据通信协议,于服务端收发数据
3、处理QTcpSocket的StateChange()信号,在状态栏显示socket的状态
TCP客户端代码实现
mainwindow.h的实现
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QTcpSocket *tcpClient; //socket
QLabel *LabSocketState; //状态栏显示标签
QString getLocalIP();//获取本机IP地址
protected:
void closeEvent(QCloseEvent *event);
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
//自定义槽函数
void onConnected();
void onDisconnected();
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();//读取socket传入的数据
void on_actConnect_triggered();
void on_actDisconnect_triggered();
void on_actClear_triggered();
void on_btnSend_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp的实现
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostAddress>
#include <QHostInfo>
QString MainWindow::getLocalIP()
{
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;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (tcpClient->state()==QAbstractSocket::ConnectedState)
tcpClient->disconnectFromHost();
event->accept();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpClient=new QTcpSocket(this); //创建socket变量
LabSocketState=new QLabel("Socket状态:");//状态栏标签
LabSocketState->setMinimumWidth(250);
ui->statusBar->addWidget(LabSocketState);
QString localIP=getLocalIP();//本机IP
this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);
ui->comboServer->addItem(localIP);
connect(tcpClient,SIGNAL(connected()),this,SLOT(onConnected()));
connect(tcpClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
//获取连接状态
connect(tcpClient,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
connect(tcpClient,SIGNAL(readyRead()),
this,SLOT(onSocketReadyRead()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onConnected()
{ //connected()信号槽函数
ui->plainTextEdit->appendPlainText("**已连接到服务器");
ui->plainTextEdit->appendPlainText("**peer address:"+
tcpClient->peerAddress().toString());
ui->plainTextEdit->appendPlainText("**peer port:"+
QString::number(tcpClient->peerPort()));
ui->actConnect->setEnabled(false);
ui->actDisconnect->setEnabled(true);
}
void MainWindow::onDisconnected()
{//disConnected()信号槽函数
ui->plainTextEdit->appendPlainText("**已断开与服务器的连接");
ui->actConnect->setEnabled(true);
ui->actDisconnect->setEnabled(false);
}
void MainWindow::onSocketReadyRead()
{//readyRead()信号槽函数
while(tcpClient->canReadLine())
ui->plainTextEdit->appendPlainText("[in] "+tcpClient->readLine());
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{//stateChange()信号槽函数
switch(socketState)
{
case QAbstractSocket::UnconnectedState:
LabSocketState->setText("scoket状态: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");
}
}
void MainWindow::on_actConnect_triggered()
{//连接到服务器
QString addr=ui->comboServer->currentText();
quint16 port=ui->spinPort->value();
tcpClient->connectToHost(addr,port);
// tcpClient->connectToHost(QHostAddress::LocalHost,port);
}
void MainWindow::on_actDisconnect_triggered()
{//断开与服务器的连接
if (tcpClient->state()==QAbstractSocket::ConnectedState)
tcpClient->disconnectFromHost();
}
void MainWindow::on_actClear_triggered()
{
ui->plainTextEdit->clear();
}
void MainWindow::on_btnSend_clicked()
{//发送数据
QString msg=ui->editMsg->text();
ui->plainTextEdit->appendPlainText("[out] "+msg);
ui->editMsg->clear();
ui->editMsg->setFocus();
QByteArray str=msg.toUtf8();
str.append('\n');
tcpClient->write(str);
}
程序运行结果如图所示
总结
服务器和客户端的实现只是简单的演示了TCP通信的基本原理,TCPServer只允许一个TCPClient客户端接入。而一般的TCP服务器程序允许多个客户端接入。为了使每个socket连接独立通信不影响,一般采用多线程,既为一个socket连接创建一个线程
lient->write(str);
}
#### 程序运行结果如图所示
[外链图片转存中...(img-ldEvMlvq-1687778801285)]
### 总结
服务器和客户端的实现只是简单的演示了TCP通信的基本原理,TCPServer只允许一个TCPClient客户端接入。而一般的TCP服务器程序允许多个客户端接入。为了使每个socket连接独立通信不影响,一般采用多线程,既为一个socket连接创建一个线程
以上实例之间的数据通信采用基于行的数据通信协议,只能传输字符串数据。QTcpSocket间接继承QIODevice,可以使用数据流的方式传输二进制数据流,列如传输图片、任意格式文件等。但是这涉及到服务端和客户端之间通信协议的定义。