Qt TCP网络通信学习

Octavia ·
更新时间:2024-09-20
· 1972 次阅读

TCP简介:

Transmission Control Protocol,传输控制协议 。用于数据传输的低层网络协议,多个物联网协议都是基于TCP协议的。它是一个面向数据流和连接的可靠传输协议。

TCP头部格式:

QTcpSocket类为TCP提供了一个接口,继承自QAbstractSocket。可实现POP3、SMTP、NNTP等标准的网络协议,也可以实现自定义的网络协议。异步工作,依靠事件循环来检测到来的数据,并且自动刷新输出的数据。而QAbstractSocket继承自QIODevice,故该类具备文件的读写功能,可以使用QTextStream和QDataStream。

QHostAddress QAbstractSocket::peerAddress () const  获取主机的IP地址

quint16 QAbstractSocket::peerPort () const  获取主机的端口号

qint64 QIODevice::write ( const QByteArray & byteArray )  //写入数据,即通过TCP发送数据。

QByteArray QIODevice::read ( qint64 maxSize )   //读取数据,最多读取maxSize。即获取TCP接收的存放在缓冲区的数据

QByteArray QIODevice::readAll ()  //获取TCP存放在缓冲区可读取的所有数据。

从一个QTcpSocket中读取数据前,必须先调用qint64 QIODevice::bytesAvailable () const 函数来确保已经有足够的数据可用。

涉及到的信号:

void QAbstractSocket::connected () [signal]  当连接建立成功发射连接信号,指示一个已建立的新连接。

void QAbstractSocket::error ( QAbstractSocket::SocketError socketError ) [signal]  连接发生了错误,就会发送error()信号,参数指示发生了什么错误。

void QAbstractSocket::disconnected () [signal]  断开一个已经存在的连接时,发射断开信号。

void QAbstractSocket::stateChanged ( QAbstractSocket::SocketState socketState ) [signal]  状态改变都会发射stateChanged()信号。

void QIODevice::bytesWritten ( qint64 bytes ) [signal]  表示数据写入完成,对应的可以调用bytesToWrite()方法了解写入了多少字节的数据。

void QIODevice::readyRead () [signal]  表示有数据可以读取,对应的可以调用bytesAvailable()方法了解可以读取多少字节的数据。

一个简单的TCP客户端和服务端程序,单连接。

客户端程序:

#ifndef QTTCPCLIENT_H #define QTTCPCLIENT_H #include <QObject> #include <QAbstractSocket> class QTcpSocket; //前置声明 //客户端程序  单连接,即一个客户端一个服务端 class QtTcpClient : public QObject {     Q_OBJECT public:     explicit QtTcpClient(QObject *parent = 0);     ~QtTcpClient();     void sendMessage(); //发送信息 private slots:     void readMessage();  //获取返回的信息     void displayError(QAbstractSocket::SocketError);  //获取连接发生的错误 private:     QTcpSocket *tcpSocket;  //tcp连接     quint16 blockSize;  //发送数据的大小 }; #endif // QTTCPCLIENT_H #include "qttcpclient.h" #include <QtNetwork> #include <QDataStream> #include <QString> #include <QByteArray> #include <QIODevice> #include <QObject> #include <QDebug> QtTcpClient::QtTcpClient(QObject *parent) :     QObject(parent) {     tcpSocket=new QTcpSocket(this);     //建立信号连接,readyRead表示有数据过来,需要读取     connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readMessage()));     //建立信号连接,error(QAbstractSocket::SocketError)连接发生错误或关闭时会发射此信号     connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));     blockSize=0;     tcpSocket->abort();//终止已有的连接     tcpSocket->connectToHost(QHostAddress(QString("127.0.0.1")),6666);//建立新的连接     qDebug()<<"client run......"<<endl; } QtTcpClient::~QtTcpClient() {     delete tcpSocket;     tcpSocket=NULL; } void QtTcpClient::sendMessage() {     QByteArray block;     QDataStream out(&block,QIODevice::WriteOnly);     out.setVersion(QDataStream::Qt_4_0);     out<<(quint16)0;     out<<QString("hi server this is my first connect!!!");     out.device()->seek(0);//定位到数据的开头     out<<(quint16)(block.size()-sizeof(quint16)); //添加数据大小信息到数据开头     tcpSocket->write(block); //发送数据 } void QtTcpClient::readMessage() {     QString message;     QDataStream in(tcpSocket);     in.setVersion(QDataStream::Qt_4_0);     if(blockSize==0)     {         //判断接收的数据是否大于两字节,也就是文件的大小信息所占的空间         //如果是则保存到blockSize中,否则直接返回,继续接收数据。         if(tcpSocket->bytesAvailable()<(int)sizeof(quint16))         {             return;         }         in>>blockSize;     }     //如果没有接收完全部数据,则返回继续接收     if(tcpSocket->bytesAvailable()<blockSize)     {         return;     }     //将接收的数据存放到变量中     in>>message;     qDebug()<<message;     this->sendMessage();     //断开连接     tcpSocket->disconnectFromHost(); } void QtTcpClient::displayError(QAbstractSocket::SocketError) {     switch(tcpSocket->error())     {     case QAbstractSocket::RemoteHostClosedError:  //远程服务端关闭连接错误         tcpSocket->disconnectFromHost();         qDebug()<<"client close now";         break;     default:         qDebug()<<"error id: "<<tcpSocket->error()<<endl;         qDebug()<<"error message: "<<tcpSocket->errorString()<<endl;         break;     } } #include <QCoreApplication> #include "qttcpclient.h" int main(int argc, char *argv[]) {     QCoreApplication a(argc, argv);     QtTcpClient tcpclient_t;     return a.exec(); }

服务端程序:

#ifndef QTTCPSERVER_H #define QTTCPSERVER_H #include <QObject> #include <QAbstractSocket> class QTcpServer; //前置声明 class QTcpSocket; //服务端程序  单连接,即一个客户端一个服务端 class QtTcpServer : public QObject {     Q_OBJECT public:     explicit QtTcpServer(QObject *parent = 0);     ~QtTcpServer(); private slots:     void sendMessage();//发送信息     void readMessage();//获取返回的信息     void displayError(QAbstractSocket::SocketError);//获取连接发生的错误 private:     QTcpServer *tcpServer; //tcp服务端     QTcpSocket *clientConnect;//来自客户端的连接     quint16 blockSize;  //接收数据的大小 }; #endif // QTTCPSERVER_H #include "qttcpserver.h" #include <QtNetwork> #include <QDataStream> #include <QString> #include <QByteArray> #include <QIODevice> #include <QObject> #include <QDebug> QtTcpServer::QtTcpServer(QObject *parent) :     QObject(parent) {     blockSize=0;     tcpServer=new QTcpServer(this);     //开始监听     if(!tcpServer->listen(QHostAddress(QString("127.0.0.1")),6666))     {         qDebug()<<tcpServer->serverError()<<endl;         qDebug()<<tcpServer->errorString()<<endl;         qApp->exit();     }     //建立信号连接,每来一个新的连接,就发送服务端信息     connect(tcpServer,SIGNAL(newConnection()),this,SLOT(sendMessage()));     qDebug()<<"server run....."<<endl; } QtTcpServer::~QtTcpServer() {     delete tcpServer;     tcpServer=NULL;     delete clientConnect;     clientConnect=NULL; } void QtTcpServer::sendMessage() {     QByteArray block;     QDataStream out(&block,QIODevice::WriteOnly);     out.setVersion(QDataStream::Qt_4_0);     out<<(quint16)0;     out<<QString("hello client the connect build!!!");     out.device()->seek(0);     out<<(quint16)(block.size()-sizeof(quint16));     clientConnect=tcpServer->nextPendingConnection();//获取客户端的连接     tcpServer->close();     //关联套接字的disconnected()和deleteLater(),表明当连接断开时删除该套接字     connect(clientConnect,SIGNAL(disconnected()),clientConnect,SLOT(deleteLater()));     clientConnect->write(block);     connect(clientConnect,SIGNAL(readyRead()),this,SLOT(readMessage()));     connect(clientConnect,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError))); } void QtTcpServer::readMessage() {     QString message;     QDataStream in(clientConnect);     in.setVersion(QDataStream::Qt_4_0);     if(blockSize==0)     {         if(clientConnect->bytesAvailable()<(int)sizeof(quint16))         {             return;         }         in>>blockSize;     }     if(clientConnect->bytesAvailable()<blockSize)     {         return;     }     in>>message;     qDebug()<<message; } void QtTcpServer::displayError(QAbstractSocket::SocketError) {     switch(clientConnect->error())     {     case QAbstractSocket::RemoteHostClosedError:         clientConnect->disconnectFromHost(); //disconnectFromHost函数会一直等待套接字将所有数据发送完毕,然后关闭该套接字,并发射disconnected信号         qDebug()<<"server connect close"<<endl;         break;     default:         qDebug()<<"error id: "<<clientConnect->error()<<endl;         qDebug()<<"error message: "<<clientConnect->errorString()<<endl;         clientConnect->disconnectFromHost();         qDebug()<<"server connect close"<<endl;         break;     } } #include <QCoreApplication> #include "qttcpserver.h" int main(int argc, char *argv[]) {     QCoreApplication a(argc, argv);     QtTcpServer tcpserver_t;     return a.exec(); }

运行结果:

基于TCP的文件传输程序:

客户端程序:

#ifndef CLIENT_H #define CLIENT_H #include <QAbstractSocket> #include <QDialog> #include <QFile> #include <QTcpSocket> #include <QString> #include <QByteArray> namespace Ui { class Client; } class Client : public QDialog {     Q_OBJECT public:     explicit Client(QWidget *parent = 0);     ~Client(); private slots:     void openFile();     void send();     void startTransfer();     void updateClientProgress(qint64);     void displayError(QAbstractSocket::SocketError);     void on_pushButton_open_clicked();     void on_pushButton_send_clicked(); private:     Ui::Client *ui;     QTcpSocket *tcpClient; //tcp连接     QFile *localFile;      //要发送的文件     qint64 totalBytes;     //发送数据的总大小     qint64 bytesWritten;   //已经发送的数据大小     qint64 bytesToWrite;   //剩余的数据大小     qint64 payloadSize;    //每次发送数据的大小     QString fileName;      //保存文件路径     QByteArray outBlock;   //数据缓冲区,即存放每次要发送的数据块 }; #endif // CLIENT_H #include "client.h" #include "ui_client.h" #include <QtNetwork> #include <QFileDialog> #include <QDebug> Client::Client(QWidget *parent) :     QDialog(parent),     ui(new Ui::Client) {     ui->setupUi(this);     //初始化变量     this->payloadSize=64*1024;   //64KB,每次发送的数据块大小为64KB     this->totalBytes=0;     this->bytesWritten=0;     this->bytesToWrite=0;     this->tcpClient=new QTcpSocket(this);     //当连接服务器成功时,发送connected()信号,开始传送文件     connect(this->tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));     //每次发送成功后发送bytesWritten(qint64))信号,告诉已成功发送的数据块大小,并更新进度条     connect(this->tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));     //接收tcp连接发生的错误     connect(this->tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));     ui->pushButton_send->setEnabled(false); } Client::~Client() {     delete ui; } void Client::openFile() {     this->fileName=QFileDialog::getOpenFileName(this);     if(!this->fileName.isEmpty())     {         ui->pushButton_send->setEnabled(true);         ui->label_state->setText(QString("打开文件 %1 成功").arg(this->fileName));     } } void Client::send() {     ui->pushButton_send->setEnabled(false);     this->bytesWritten=0;     ui->label_state->setText(QString("连接中..."));     //连接服务器     this->tcpClient->connectToHost(ui->lineEdit_host->text(),ui->lineEdit_port->text().toInt()); } void Client::startTransfer() {     this->localFile=new QFile(this->fileName);     //打开文件     if(!this->localFile->open(QFile::ReadOnly))     {         qDebug()<<"client: open file error!"<<endl;         return;     }     //获取打开文件的大小     this->totalBytes=this->localFile->size();     QDataStream sendOut(&this->outBlock,QIODevice::WriteOnly);     sendOut.setVersion(QDataStream::Qt_4_0);     //获取文件名(去掉文件前面的路径)     QString currentFileName=this->fileName.right(this->fileName.size()-this->fileName.lastIndexOf('/')-1);     //保留总大小信息空间,文件名大小信息空间,然后输入文件名     sendOut<<qint64(0)<<qint64(0)<<currentFileName;     //总大小变量为总大小信息、文件名大小信息、文件名和实际文件大小的总和     this->totalBytes+=this->outBlock.size();     //返回outBlock的开始,填入总大小信息     sendOut.device()->seek(0);     //填入各个项的大小信息以及文件名     sendOut<<this->totalBytes<<qint64(this->outBlock.size()-sizeof(qint64)*2);     //发送完文件头结构后剩余数据的大小     this->bytesToWrite=this->totalBytes-this->tcpClient->write(this->outBlock);     ui->label_state->setText(QString("已连接"));     this->outBlock.resize(0); } void Client::updateClientProgress(qint64 numBytes) {     //更新已经发送数据的大小     this->bytesWritten+=(int)numBytes;     //如果已经发送了数据     if(this->bytesToWrite>0)     {         //每次发送payloadSize大小的数据,不足就发送剩余数据大小         this->outBlock=this->localFile->read(qMin(this->bytesToWrite,this->payloadSize));         //发送完一次数据后还剩余数据的大小         this->bytesToWrite-=(int)this->tcpClient->write(this->outBlock);         //清空发送缓冲区         this->outBlock.resize(0);     }     else     {         //如果没有发送任何数据,就关闭文件         this->localFile->close();     }     //更新进度条     ui->progressBar->setMaximum(this->totalBytes);     ui->progressBar->setValue(this->bytesWritten);     //发送完毕     if(this->bytesWritten==this->totalBytes)     {         ui->label_state->setText(QString("传送文件 %1 成功").arg(this->fileName));         this->localFile->close(); //关闭文件         this->tcpClient->close(); //关闭tcp连接     } } void Client::displayError(QAbstractSocket::SocketError) {     qDebug()<<this->tcpClient->errorString()<<endl;     this->tcpClient->close();     ui->progressBar->reset();     ui->label_state->setText(QString("客户端已就绪!"));     ui->pushButton_send->setEnabled(true); } void Client::on_pushButton_open_clicked() {     ui->progressBar->reset();     ui->label_state->setText(QString("状态:等待文件打开!"));     this->openFile(); } void Client::on_pushButton_send_clicked() {     this->send(); } #include <QApplication> #include "client.h" #include <QTextCodec> int main(int argc, char *argv[]) {     QApplication a(argc, argv);     QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());     QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());     QTextCodec::setCodecForTr(QTextCodec::codecForLocale());     Client w;     w.show();     return a.exec(); }

服务器程序:

#ifndef SERVER_H #define SERVER_H #include <QDialog> #include <QFile> #include <QTcpSocket> #include <QTcpServer> #include <QAbstractSocket> #include <QString> #include <QByteArray> namespace Ui { class Server; } class Server : public QDialog {     Q_OBJECT public:     explicit Server(QWidget *parent = 0);     ~Server(); private slots:     void start();     void acceptConnection();     void updateServerProgress();     void displayError(QAbstractSocket::SocketError socketError);     void on_pushButton_clicked(); private:     Ui::Server *ui;     QTcpServer tcpServer; //服务器监听     QTcpSocket *tcpServerConnection; //来自客户端的连接     qint64 totalBytes;  //存放总大小信息     qint64 bytesReceived; //已收到的数据大小     qint64 fileNameSize; //存放文件名的大小信息     QString fileName;  //存放文件名     QFile *localFile;  //本地文件     QByteArray inBlock;  //数据缓冲区 }; #endif // SERVER_H #include "server.h" #include "ui_server.h" #include <QtNetwork> #include <QDebug> Server::Server(QWidget *parent) :     QDialog(parent),     ui(new Ui::Server) {     ui->setupUi(this);     //有新的连接到来,发射newConnection()信号,获取新的连接     connect(&this->tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection())); } Server::~Server() {     delete ui; } void Server::start() {     //建立监听     if(!this->tcpServer.listen(QHostAddress(QString("127.0.0.1")),6666))     {         qDebug()<<this->tcpServer.errorString()<<endl;         close();         return;     }     ui->pushButton->setEnabled(false);     //初始化变量     this->totalBytes=0;     this->bytesReceived=0;     this->fileNameSize=0;     ui->label->setText(QString("监听"));     ui->progressBar->reset(); } void Server::acceptConnection() {     //获取来自客户端的连接     this->tcpServerConnection=this->tcpServer.nextPendingConnection();     //建立信号连接     connect(this->tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));     connect(this->tcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));     ui->label->setText(QString("接收连接"));     this->tcpServer.close(); } void Server::updateServerProgress() {     QDataStream in(this->tcpServerConnection);     in.setVersion(QDataStream::Qt_4_0);     //如果接收到的数据小于16个字节,保存到来的文件头结构     if(this->bytesReceived<=sizeof(qint64)*2)     {         if((this->tcpServerConnection->bytesAvailable()>=sizeof(qint64)*2)&&(this->fileNameSize==0))         {             //接收数据总大小信息和文件名大小信息             in>>this->totalBytes>>this->fileNameSize;             this->bytesReceived+=sizeof(qint64)*2;         }         if((this->tcpServerConnection->bytesAvailable()>=this->fileNameSize)&&(this->fileNameSize!=0))         {             //接收文件名,并建立文件             in>>this->fileName;             ui->label->setText(QString("接收文件 %1 ......").arg(this->fileName));             this->bytesReceived+=this->fileNameSize;             this->localFile=new QFile(this->fileName);             if(!this->localFile->open(QFile::WriteOnly))             {                 qDebug()<<"server: open file error"<<endl;                 return;             }         }         else         {             return;         }     }     //开始接收文件里面的数据     if(this->bytesReceived<this->totalBytes)     {         this->bytesReceived+=this->tcpServerConnection->bytesAvailable();         this->inBlock=this->tcpServerConnection->readAll();         this->localFile->write(this->inBlock);         this->inBlock.resize(0);      }     ui->progressBar->setMaximum(this->totalBytes);     ui->progressBar->setValue(this->bytesReceived);     //接收数据完成时     if(this->bytesReceived==this->totalBytes)     {         this->tcpServerConnection->close();         this->localFile->close();         ui->pushButton->setEnabled(true);         ui->label->setText(QString("接收文件 %1 成功").arg(this->fileName));     } } void Server::displayError(QAbstractSocket::SocketError socketError) {     qDebug()<<this->tcpServerConnection->errorString();     this->tcpServerConnection->close();     ui->progressBar->reset();     ui->label->setText(QString("服务端就绪"));     ui->pushButton->setEnabled(true); } void Server::on_pushButton_clicked() {     start(); } #include <QApplication> #include "server.h" #include <QTextCodec> int main(int argc, char *argv[]) {     QApplication a(argc, argv);     QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());     QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());     QTextCodec::setCodecForTr(QTextCodec::codecForLocale());     Server w;     w.show();     return a.exec(); }

运行结果:



tcp 学习 通信

需要 登录 后方可回复, 如果你还没有账号请 注册新账号