1、TCP简介
TCP是一个用于数据传输的低层的网络通信协议,HTTP、TCP这些互联网协议都是基于TCP协议的。TCP是一个面向数据流和链接的可靠传输协议。QTcpSocket类为TCP提供了一个接口,该类也继承自QAbstractSocket。可以使用QTcpSocket来实现POP3、SMTP、NNTP等标准的网络协议,也可以实现自定义的网络协议。与QUdpSocket传输的数据包不同,QTcpSocket传输是连续的数据流,他尤其适合于连续的数据传输。TCP编程一般分为客户端和服务器端也就是C/S模型。
2、TCP编程
(1)、在任何数据传输之前,都必须建立一个TCP连接到远程的主机和端口上,一旦连接被建立,主机(使用TCP协议连接在一起的主机统称为peer)的IP地址和端口可以分别使用。QTcpSocket::peerAddress()和QTcpSocket::peerPort()来获取。在任何时间,peer都可以关闭连接,这时候数据传输就会立即停止。
(2)QTcpSocket是异步进行工作的,需要通过发射信号来报告状态改变和错误信息。可以使用QTcpSocket::write()来写数据,使用QTcpSocket::read()来读数据。QTcpSocket代表了两个独立的数据流:一个用来读出,一个用来写入。因为QTcpSocket继承于QIODevice,所以可以用QTextStream和QDataStream。当一个从一个QTcpSocket中读取数据之前,必须先调用QTcpSocket::bytesAvailable()函数来确保已经有足够的数据可用。
(3)如何处理到来的TCP连接?
处理到来的TCP连接一般发生在服务器端的应用程序中,可以使用QTcpServer类。调用listen()来设置服务器,然后关联newConnection()信号,每当客户端连接时候都会发射该信号,在槽函数中调用nextPendingConnection()来接收这个连接,然后使用该函数返回的QTcpSocket对象与客户端进行通信。
(4)虽然QTcpSocket大部分的函数都是一部工作的,其实QTcpSocket也可以实现同步工作(如:阻塞),调用QTcpSocket的以waitfor开头的函数,他们会挂起调用的线程,直到一个信号被发射。如:在调用QTcpSocket::connectToHost()非阻塞函数后,调用QTCPSocket::waitforConnected()来阻塞线程,直到connected()信号被发射。使用waitfor函数阻塞时事件将不会被处理,如果一个GUI线程中,用户界面就会出现冻结现象,所有一般建议不再GUI线程中使用同步套接字。
3、TCP编程实现发送一个字符串
例程功能:服务器一直监听一个端口,一旦有客户端连接请求,便建立连接,并向客户端发送一个字符串,然后客户端接收该字符串并显示出来。
服务器端:
//初始化服务器
QTcpServer *tcpServer=new QTcpServer(this);
if(!tcpServer->listern(QHostAddress::LocalHost,6666))//使用了IPv4的本地主机地址
{
qDebug()<<tcpServer->errorString();
Close();
}
//一旦服务器监听到新的请求连接信号,服务器就会发射相应的数据到客户端
Connect(tcpServer,&QTcpServer::newConnection,this,&Server::sendMessage);
Void Server::sendMessage()
{
QByteArray block;//用于暂存要发送的数据
QDataStream out(&block,QIODevice::WriteOnly);//创建数据流
Out.setVersion(QDataStream::Qt_5_8);//设置数据流版本,客户端和服务器端必需使 用一样的数据流版本
out.<<(qint16)0;//为数据流前两个字节赋值为0
out<<”hello world!!!”;//为数据流赋值(带传输的字符串)
out.device()->seek(0);//跳转到数据流最前面位置
out<<(qint16)(block.size()-sizeof(qint16));//为数据流最前面的两个字节赋值要传输的字符串字节数
QTcpSocket *clientConnection=tcpServer->nextPendingConnection();//获取已经建立的链接的套接字
Connect(clientConnection,&QTcpSocket::disconnection,clientConnection,&QTcpSocket::deleteLater);//关联disconnection信号和deleteLater槽函数,当断开时删除套接字
clientConnection->write(block);//发射字符串
clientConnection->disconnectFromHost();//断开连接而调用disconnectFromHost()函数会一直等待套接字将所有数据发送完毕,然后关闭该套接字,并发射disconnected()信号。
}
客户端:
初始化:
QTcpSocket *tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::readyRead, this, &Client::readMessage);
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this,
SLOT(displayError(QAbstractSocket::SocketError)));
连接按键槽函数:
blockSize = 0; // 初始化数据大小信息为0
tcpSocket->abort(); // 取消已有的连接
tcpSocket->connectToHost(ui->hostLineEdit->text(), ui->portLineEdit->text().toInt());
读取数据的槽:
void Client::readMessage()
{
QDataStream in(tcpSocket);//创建数据流
in.setVersion(QDataStream::Qt_5_8); // 设置数据流版本,这里要和服务器端相同
if (blockSize == 0) { // 如果是刚开始接收数据
//判断接收的数据是否大于两字节,也就是文件的大小信息所占的空间
//如果是则保存到blockSize变量中,否则直接返回,继续接收数据
if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;
in >> blockSize;
}
if(tcpSocket->bytesAvailable() < blockSize) return; // 如果没有得到全部的数据,则 返回,继续接收数据
in >> message; // 将接收到的数据存放到变量中
ui->messageLabel->setText(message); // 显示接收到的数据
}
Note :TCP编程实现传输一个文件与之类似