Use QTcpSocket and QTcpServer to transfer large files

Qt found in the actual use of Tcp communication that the sending end and the receiving end are not in one-to-one correspondence, and there will be a situation where sending multiple times only corresponds to one time, and when the speed of the sending end far exceeds the receiving end, the program will crash. Small files do not have such a problem and can be ignored. The reason why large files are sent is that the root of the problem lies in the sticky packets caused by the inconsistency between the sending and receiving ends of Tcp.
Therefore, the protocol can be formulated according to the actual situation, and data transmission can be performed in a question-and-answer manner, sacrificing efficiency to meet stability and security reliability.

The client code is as follows

TcpClientPro::TcpClientPro(QWidget *parent)
    : QWidget(parent)
{<!-- -->
    ui. setupUi(this);
initClient();
}
TcpClientPro::~TcpClientPro()
{<!-- -->
qDebug() << "~ClientPro()----------------------------";
if (m_client->state() == QAbstractSocket::ConnectedState) {<!-- -->
//If using disconnectFromHost() will not reset the socket, isValid will still be true
m_client->abort();
}
}
void TcpClientPro::initClient() {<!-- -->
//Create client object
m_client = new QTcpSocket(this);
ui.selectBtn->setEnabled(false);
ui.sendBtn->setEnabled(false);
}
void TcpClientPro::on_connectBtn_clicked() {<!-- -->
if (m_client->state() == QAbstractSocket::ConnectedState) {<!-- -->
//If using disconnectFromHost() will not reset the socket, isValid will still be true
m_client->abort();
}
else if (m_client->state() == QAbstractSocket::UnconnectedState) {<!-- -->
//Read ip and port from the interface
const QHostAddress address = QHostAddress(ui. addressEt->text());
const unsigned short port = ui.portEt->text().toUShort();
\t\t//connect to the server
m_client->connectToHost(address, port);
}
else {<!-- -->
\t\t
}
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(), & amp;fdata, sizeof(fdata));//store the structure into the array
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( & amp;fdata, array.data(), sizeof(fdata));//Convert to structure
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(), & amp;r_fdata, sizeof(r_fdata));//store the structure into the array
m_client->write(r_array);
m_client->waitForBytesWritten(1000);
m_readIndex++;
qDebug() << "TcpClientPro::readFileData----" << m_readIndex;
}

The server code is as follows:

```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;
}
//Disconnect from the client
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();
//Restore the interface state after closing the server
ui.listenBtn->setText("Listen");
ui.addressEt->setEnabled(true);
ui.portEt->setEnabled(true);
}
else {<!-- -->
//You can use QHostAddress::Any to listen to the corresponding ports of all addresses
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);
//Start monitoring and judge whether it is successful
if (m_server->listen(address, port)) {<!-- -->
//If the connection is successful, modify the interface button prompt, and the address bar cannot be edited
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 returns the next pending connection as a connected QTcpSocket object
// The socket is created as a child of the server, which means that the socket is automatically deleted when the QTcpServer object is destroyed.
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( & amp;fdata, array.data(), sizeof(fdata));//Convert to structure
if (fdata.type == TransFileInfo) {<!-- -->
parseFileData(fdata);
}
else if (fdata.type == TransFileData) {<!-- -->
writeFile(fdata);
}
}
void TcpServerPro::writeFile(FileDate & amp; fdata) {<!-- -->
m_readIndex++;
ui.progressBar->setValue(m_readIndex);
if (m_readIndex == m_readCnt)//the last package
{<!-- -->
m_file.write(fdata.data, m_lastSize);
m_readIndex = 0;
m_file.close();//transmission complete
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 & amp;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();
}

The dynamic display effect is as follows: