In the previous section, we use the TCP server to send a string and then receive it on the tcp client. In this section, we re-write a client program and a server program. This time, we asked the client to send files and the server to receive files. With the foundation of the previous section, the content of this section is well understood. Pay attention to the association between several signals and slots. Of course, this time we will have a better understanding of how data is sent and received.
I. Client
This time, we will first explain the client. We will connect to the server in the client. Once the connection is successful, the connected () signal will be sent, and then the file will be sent.
As we can see in the previous section, we first sent the data size information when sending the data. This time, we need to first send the total size of the file, then the file name length, and then the file name. These three parts are collectively called the file header structure, and finally the file data is sent. Therefore, in the sending function, we need to process it accordingly. Of course, in the receiving function of the server, we also need to process it accordingly. For the file size, qint64 is used this time. It is 64-bit and can represent a large file.
1.In the previous section, we created a new project and named it "tcpsender ".". Note: Add a networkModule.
2.In the widget. UIThe interface is designed as follows.
Here, the objectname of line edit after "host" is hostlineedit; The objectname of line edit after "Port" is portlineedit; The objectname of the following progress bar is clientprogressbar, the value attribute is set to 0; the "status" label's objetname is clientstatuslabel; the "open" button's objectname is openbutton; the "send" button's objectname is sendbutton;
3.In the widget. hFile.
(1) Add a header file # include <qtnetwork>
(2) Add the private variable:
Qtcpsocket * tcpclient;
Qfile * localfile; // the file to be sent
Qint64 totalbytes; // total data size
Qint64 byteswritten; // data size sent
Qint64 bytestowrite; // remaining data size
Qint64 loadsize; // the size of data sent each time
Qstring filename; // save the file path
Qbytearray outblock; // data buffer, that is, the data to be sent each time.
(3) Add a private slot function:
Private slots:
Void send (); // connect to the server
Void starttransfer (); // sends the file size and other information
Void updateclientprogress (qint64); // send data, update progress bar
Void displayerror (q1_actsocket: socketerror); // Display Error
Void openfile (); // open the file
4.InWidget. cppFile.
Add header file: # include <qfiledialog>
(1) Add code to the constructor:
Loadsize = 4*1024;
Totalbytes = 0;
Byteswritten = 0;
Bytestowrite = 0;
Tcpclient = new qtcpsocket (this );
Connect (tcpclient, signal (connected (), this, slot (starttransfer ()));
// When the connection to the server is successful, send the connected () signal and we start to transmit the file
Connect (tcpclient, signal (byteswritten (qint64), this,
Slot (updateclientprogress (qint64 )));
// Update the progress bar when data is successfully sent.
Connect (tcpclient, signal (error (q1_actsocket: socketerror), this,
Slot (displayerror (q1_actsocket: socketerror )));
UI-> sendbutton-> setenabled (false );
// Start to make the send button unavailable
We initialize the variables and associate several signals with the slot functions.
(2) Implement the file opening function.
Void Widget: openfile () // open the file
{
Filename = qfiledialog: getopenfilename (this );
If (! Filename. isempty ())
{
UI-> sendbutton-> setenabled (true );
UI-> clientstatuslabel-> settext (TR ("opened file % 1 successful !")
. Arg (filename ));
}
}
This function will be called by clicking the OPEN button in the event slot function below.
(3) Implement the connection function.
Void Widget: Send () // connect to the server and execute send
{
UI-> sendbutton-> setenabled (false );
Byteswritten = 0;
// Initialize the sent byte to 0
UI-> clientstatuslabel-> settext (TR ("connected ..."));
Tcpclient-> connecttohost (ui-> hostlineedit-> text (),
UI-> portlineedit-> text (). toint (); // connect
}
This function is called by clicking the event slot function in the send button.
(4) Implement sending of the file header structure.
Void Widget: starttransfer () // send file size and other information
{
localFile = new QFile(fileName);
if(!localFile->open(QFile::ReadOnly))
{
qDebug() << "open file error!";
return;
}
totalBytes = localFile->size();
// Total file size
QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_4_6);
QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
sendOut << qint64(0) << qint64(0) << currentFileName;
// Write the total size information space, file name size information space, and file name in sequence
totalBytes += outBlock.size();
// The total size here is the sum of the file name size and other information and the actual file size.
sendOut.device()->seek(0);
sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));
// Return the start of outbolock and replace the two qint64 (0) spaces with the actual size information
bytesToWrite = totalBytes - tcpClient->write(outBlock);
// Size of the remaining data after sending the header data
UI-> clientstatuslabel-> settext (TR ("connected "));
outBlock.resize(0);
}
(5) The following is the update progress bar, that is, sending file data.
Void Widget: updateclientprogress (qint64 numbytes) // update the progress bar to transfer files
{
Byteswritten + = (INT) numbytes;
// Size of the sent data
If (bytestowrite> 0) // If data has been sent
{
Outblock = localfile-> Read (qmin (bytestowrite, loadsize ));
// Set the size of the loadsize data to 4 kb each time. If the remaining data is less than 4 kb,
// The size of the remaining data to be sent
Bytestowrite-= (INT) tcpclient-> write (outblock );
// The size of the remaining data after sending the data once
Outblock. Resize (0 );
// Clear the sending Buffer
}
Else
{
Localfile-> close (); // close the file if no data is sent
}
UI-> clientprogressbar-> setmaximum (totalbytes );
UI-> clientprogressbar-> setvalue (byteswritten );
// Update the progress bar
If (byteswritten = totalbytes) // The message is sent
{
UI-> clientstatuslabel-> settext (TR ("Transfer File % 1 successful"). Arg (filename ));
Localfile-> close ();
Tcpclient-> close ();
}
}
(6) Implement error handling functions.
Void Widget: displayerror (q1_actsocket: socketerror) // Display Error
{
Qdebug () <tcpclient-> errorstring ();
Tcpclient-> close ();
UI-> clientprogressbar-> Reset ();
UI-> clientstatuslabel-> settext (TR ("client ready "));
UI-> sendbutton-> setenabled (true );
}
(7) from the widget. UI, click the event slot function of the "open" button and "send" button respectively, and then change it as follows.
Void Widget: on_openbutton_clicked () // open the button
{
Openfile ();
}
Void Widget: on_sendbutton_clicked () // send button
{
Send ();
}
5.In order to make the Chinese characters in the program do not display garbled characters, we can change them in the main. cpp file.
Add header file: # include <qtextcodec>
Add the code in the main function: qtextcodec: setcodecfortr (qtextcodec: codecforlocale ());
6.Run the program as follows.
7.Overall Program thinking analysis.
We have designed the interface, and then press the "open" button to select the file to be sent, then the openfile () function is called. Then, click the send button to call the send () function to connect to the server. When the connection is successful, the connected () signal is sent. Then, the starttransfer () function is executed to send the file header structure. When the connection is successful, the byteswritten (qint64) signal is sent, at this time, we execute updateclientprogress (qint64 numbytes) to transfer file data and update the progress bar. A loadsize variable is used here. In the constructor, We initialize it to 4*1024, that is, 4 bytes. Its function is, we divide the entire large file into many small parts for sending, each of which is divided into 4 bytes. When the connection fails, an error (qiniactsocket: socketerror) is sent, and the displayerror () function is executed. We will not analyze other details in the program. I hope you can study the program on your own.
Ii. server side.
We receive data on the server. The server program is very simple. We start to listen. Once we find a connection request, we send a newconnection () signal. Then we accept the connection and start to receive data.
1.Create a project named "tcpreceiver".
2.Let's change the widget. UI.File, the design interface is as follows.
The objectname of the "server" label is serverstatuslabel, The objectname of the progress bar is serverprogressbar, and its value attribute is set to 0; The objectname of the "Start listening" button is startbutton.
The effect is as follows.
3.Modify widget. hFile Content.
(1) Add a header file: # include <qtnetwork>
(2) Add private variables:
Qtcpserver tcpserver;
Qtcpsocket * tcpserverconnection;
Qint64 totalbytes; // stores the total size information
Qint64 bytesreceived; // the size of the received data
Qint64 filenamesize; // file name size information
Qstring filename; // file name
Qfile * localfile; // local file
Qbytearray inblock; // data buffer
(3) Add a private slot function:
Private slots:
Void on_startbutton_clicked ();
Void start (); // start listening
Void acceptconnection (); // establish a connection
Void updateserverprogress (); // update the progress bar to receive data
Void displayerror (q1_actsocket: socketerror );
// Display Error
4.Modify widget. cppFile.
(1) Add code to the constructor:
Totalbytes = 0;
Bytesreceived = 0;
Filenamesize = 0;
Connect (& tcpserver, signal (newconnection (), this,
Slot (acceptconnection ()));
// Send a newconnection () signal when a new connection is found
(2) Implement the START () function.
Void Widget: Start () // start listening
{
UI-> startbutton-> setenabled (false );
Bytesreceived = 0;
If (! Tcpserver. Listen (qhostaddress: localhost, 6666 ))
{
Qdebug () <tcpserver. errorstring ();
Close ();
Return;
}
UI-> serverstatuslabel-> settext (TR ("listener "));
}
(3) Accept the connection function.
Void Widget: acceptconnection () // accept the connection
{
Tcpserverconnection = tcpserver. nextpendingconnection ();
Connect (tcpserverconnection, signal (readyread (), this,
Slot (updateserverprogress ()));
Connect (tcpserverconnection,
Signal (error (q1_actsocket: socketerror), this,
Slot (displayerror (q1_actsocket: socketerror )));
UI-> serverstatuslabel-> settext (TR ("Accept connection "));
Tcpserver. Close ();
}
(4) Implement the update progress bar function.
Void Widget: updateserverprogress () // update progress bar to receive data
{
Qdatastream in (tcpserverconnection );
In. setversion (qdatastream: qt_4_6 );
If (bytesreceived <= sizeof (qint64) * 2)
{// If the received data is smaller than 16 bytes, It is the header file information that we saved to the beginning to receive the data.
If (tcpserverconnection-> bytesavailable ()> = sizeof (qint64) * 2)
& (Filenamesize = 0 ))
{// Total size of received data and file name size
In> totalbytes> filenamesize;
Bytesreceived + = sizeof (qint64) * 2;
}
If (tcpserverconnection-> bytesavailable ()> = filenamesize)
& (Filenamesize! = 0 ))
{// Receives the file name and creates a file
In> filename;
UI-> serverstatuslabel-> settext (TR ("received file % 1 ...")
. Arg (filename ));
Bytesreceived + = filenamesize;
Localfile = new qfile (filename );
If (! Localfile-> open (qfile: writeonly ))
{
Qdebug () <"Open File Error !";
Return;
}
}
Else return;
}
If (bytesreceived <totalbytes)
{// If the received data is smaller than the total data, write the data to the file
Bytesreceived + = tcpserverconnection-> bytesavailable ();
Inblock = tcpserverconnection-> readall ();
Localfile-> write (inblock );
Inblock. Resize (0 );
}
UI-> serverprogressbar-> setmaximum (totalbytes );
UI-> serverprogressbar-> setvalue (bytesreceived );
// Update the progress bar
If (bytesreceived = totalbytes)
{// When receiving data is complete
Tcpserverconnection-> close ();
Localfile-> close ();
UI-> startbutton-> setenabled (true );
UI-> serverstatuslabel-> settext (TR ("received file % 1 successful !")
. Arg (filename ));
}
}
(5) error handling functions.
Void Widget: displayerror (q1_actsocket: socketerror) // handle errors
{
Qdebug () <tcpserverconnection-> errorstring ();
Tcpserverconnection-> close ();
UI-> serverprogressbar-> Reset ();
UI-> serverstatuslabel-> settext (TR ("Server Ready "));
UI-> startbutton-> setenabled (true );
}
(6) In the widget. UI, go to the "Start listening" button and click the event slot function. The changes are as follows.
Void Widget: on_startbutton_clicked () // start listening button
{
Start ();
}
5.In order to make the Chinese characters in the program do not display garbled characters, we can change them in the main. cpp file.
Add header file: # include <qtextcodec>
Add the code in the main function: qtextcodec: setcodecfortr (qtextcodec: codecforlocale ());
6.Run the program and run the tcpsender program at the same time. The effect is as follows.
Press the start listener button on the server, enter the host address and port number on the client, open the file to be sent, and click send.
We have introduced the TCP application in these two sections. We can see that both the server and client can be used as the sender or receiver, in addition, as long as the corresponding protocol is used for sending and receiving data, it can be programmed according to the user's needs, without a fixed format.