使用QTcpSocket及QTcpServer传输大文件
Qt在实际的使用Tcp通信中发现,发送端与接收端并不是一一对应的,会出现发送多次只相应一次的情况,且发送端速度远超接收端时会引起程序崩溃,小文件不存在这样的问题,可忽略,大文件发送之所以出现,其问题的根本点在于Tcp发送与接收端不一致引起的粘包。
因此可根据实际情况制定协议,使用一问一答的方式进行数据传输,牺牲效率以满足稳定性和安全可靠性。
客户端代码如下
TcpClientPro::TcpClientPro(QWidget *parent): QWidget(parent)
{ui.setupUi(this);initClient();
}
TcpClientPro::~TcpClientPro()
{qDebug() << "~ClientPro()----------------------------";if (m_client->state() == QAbstractSocket::ConnectedState) {//如果使用disconnectFromHost()不会重置套接字,isValid还是会为truem_client->abort();}
}
void TcpClientPro::initClient() {//创建client对象m_client = new QTcpSocket(this);ui.selectBtn->setEnabled(false);ui.sendBtn->setEnabled(false);
}
void TcpClientPro::on_connectBtn_clicked() {if (m_client->state() == QAbstractSocket::ConnectedState) {//如果使用disconnectFromHost()不会重置套接字,isValid还是会为truem_client->abort();}else if (m_client->state() == QAbstractSocket::UnconnectedState) {//从界面上读取ip和端口const QHostAddress address = QHostAddress(ui.addressEt->text());const unsigned short port = ui.portEt->text().toUShort();//连接服务器m_client->connectToHost(address, port);}else {}connect(m_client, &QTcpSocket::connected, this, &TcpClientPro::connectedSlot);connect(m_client, &QTcpSocket::disconnected, this, &TcpClientPro::disconnectedSlot);connect(m_client, &QTcpSocket::readyRead, this, &TcpClientPro::readyReadSlot);
}
void TcpClientPro::on_selectBtn_clicked() {QString filePath = QFileDialog::getOpenFileName(this, "open", "../");if (!filePath.isEmpty()) {m_fileName = "";QFileInfo info(filePath);m_fileName = info.fileName();qDebug() << "m_fileName----------" << m_fileName;m_file.setFileName(filePath);if (m_file.open(QIODevice::ReadOnly)) {FileDate fdata;int size = m_file.size();if (size == 0) {m_file.close();return;}m_lastSize = size % sizeof(fdata.data);m_readNum = size / sizeof(fdata.data) + (m_lastSize > 0 ? 1 : 0);QByteArray array1 = m_fileName.toStdString().c_str();//strncpy(fdata.fileName, array1.data(), sizeof(fdata.fileName));////fdata.FileName = m_fileName;fdata.readCnt = m_readNum;fdata.lastSize = m_lastSize;fdata.type = TransFileInfo;qDebug() << "sizeof(fdata)-----------------------" << sizeof(fdata);QByteArray array;array.resize(sizeof(fdata));memcpy(array.data(), &fdata, sizeof(fdata));//把结构体存入数组m_client->write(array);m_client->waitForBytesWritten();}}
}
void TcpClientPro::on_sendBtn_clicked() {}
void TcpClientPro::connectedSlot() {ui.connectBtn->setText("Disconnect");ui.selectBtn->setEnabled(true);ui.addressEt->setEnabled(false);ui.portEt->setEnabled(false);
}
void TcpClientPro::disconnectedSlot() {ui.connectBtn->setText("Connect");ui.addressEt->setEnabled(true);ui.portEt->setEnabled(true);ui.selectBtn->setEnabled(false);ui.sendBtn->setEnabled(false);
}
void TcpClientPro::readyReadSlot() {if (m_client->bytesAvailable() <= 0)return;FileDate fdata;QByteArray array;array = m_client->readAll();memcpy(&fdata, array.data(), sizeof(fdata));//转化到结构体if (fdata.state == CorrectState) {if (fdata.type == TransFileInfoDone) {m_readIndex = 0;readFileData();}else if (fdata.type == TransFileData) {readFileData();}if (m_readIndex == m_readNum) {m_file.close();}}else {m_file.close();}
}
void TcpClientPro::readFileData() {QByteArray r_array;FileDate r_fdata;r_array.resize(sizeof(r_fdata));r_fdata.type = TransFileData;m_file.read(r_fdata.data, sizeof(r_fdata.data));memcpy(r_array.data(), &r_fdata, sizeof(r_fdata));//把结构体存入数组m_client->write(r_array);m_client->waitForBytesWritten(1000);m_readIndex++;qDebug() << "TcpClientPro::readFileData----" << m_readIndex;
}
服务端代码如下:
```go
TcpServerPro::TcpServerPro(QWidget *parent): QWidget(parent)
{ui.setupUi(this);setWindowTitle("Server");iniServer();
}
TcpServerPro::~TcpServerPro()
{closeServer();if (m_socket != NULL) {delete m_socket;m_socket = NULL;}
}
void TcpServerPro::iniServer() {m_fileName = "";m_readCnt = 0;m_lastSize = 0;m_socket = NULL;ui.clearBtn->setEnabled(false);m_server = new QTcpServer(this);
}
void TcpServerPro::closeServer() {m_server->close();if (m_socket == NULL) {return;}//断开与客户端的连接if (m_socket->state() == QAbstractSocket::ConnectedState) {m_socket->disconnectFromHost();if (m_socket->state() != QAbstractSocket::UnconnectedState) {m_socket->abort();}}
}
void TcpServerPro::on_listenBtn_clicked() {if (m_server->isListening()) {closeServer();//关闭server后恢复界面状态ui.listenBtn->setText("Listen");ui.addressEt->setEnabled(true);ui.portEt->setEnabled(true);}else {//可以使用 QHostAddress::Any 监听所有地址的对应端口const QString address_text = ui.addressEt->text();const unsigned short port = ui.portEt->text().toUShort();const QHostAddress address = (address_text == "Any")? QHostAddress::Any: QHostAddress(address_text);//开始监听,并判断是否成功if (m_server->listen(address, port)) {//连接成功就修改界面按钮提示,以及地址栏不可编辑ui.listenBtn->setText("Close");ui.addressEt->setEnabled(false);ui.portEt->setEnabled(false);}connect(m_server, &QTcpServer::newConnection, this, &TcpServerPro::newConnectionSlot);}
}
void TcpServerPro::on_clearBtn_clicked() {ui.receiveEt->clear();
}
void TcpServerPro::newConnectionSlot() {if (m_server->hasPendingConnections()){//nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象//套接字是作为服务器的子级创建的,这意味着销毁QTcpServer对象时会自动删除该套接字。m_socket = m_server->nextPendingConnection();ui.receiveEt->append("connected.........");connect(m_socket, &QTcpSocket::readyRead, this, &TcpServerPro::readyReadSlot);ui.clearBtn->setEnabled(true);}
}
void TcpServerPro::readyReadSlot() {if (m_socket->bytesAvailable() <= 0)return;FileDate fdata;QByteArray array;//array.resize(sizeof(fdata));array = m_socket->readAll();memcpy(&fdata, array.data(), sizeof(fdata));//转化到结构体if (fdata.type == TransFileInfo) {parseFileData(fdata);}else if (fdata.type == TransFileData) {writeFile(fdata);}
}
void TcpServerPro::writeFile(FileDate& fdata) {m_readIndex++;ui.progressBar->setValue(m_readIndex);if (m_readIndex == m_readCnt)//最后一个包{m_file.write(fdata.data, m_lastSize);m_readIndex = 0;m_file.close();//传输完毕qDebug() << "transfer over------------------------";return;}else {m_file.write(fdata.data, sizeof(fdata.data));QByteArray array1;array1.resize(sizeof(fdata));FileDate buf;buf.state = CorrectState;buf.type = TransFileData;memcpy(array1.data(), &buf, sizeof(buf));qDebug() << "transfer progress-------" << double(1.0*m_readIndex / m_readCnt) * 100<<"%";m_socket->write(array1);m_socket->waitForBytesWritten(1000);}
}
void TcpServerPro::parseFileData(FileDate &fdata) {QString dirname = QFileDialog::getExistingDirectory(this, "SelectDirectory", "/");QString filename(fdata.fileName);//qDebug() << "dirname: " << dirname;QString filePath = dirname.append("/").append(filename);QFileInfo info(filePath);m_fileName = info.fileName();m_readCnt = fdata.readCnt;m_lastSize = fdata.lastSize;qDebug() << "TcpServerPro::parseFileData m_readCnt: " << m_readCnt << " m_lastSize: " << m_lastSize;m_file.setFileName(filePath);FileDate fdata1;if (m_file.open(QIODevice::WriteOnly)) {fdata1.type = TransFileInfoDone;fdata1.state = CorrectState;ui.progressBar->setMaximum(m_readCnt);ui.progressBar->setValue(0);}else {fdata1.type = TransFileData;fdata1.state = ErrorState;}m_readIndex = 0;QByteArray array1;array1.resize(sizeof(fdata1));memcpy(array1.data(), &fdata1 , sizeof(fdata1));m_socket->write(array1);m_socket->waitForBytesWritten();
}
动态显示效果如下:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
