概念:tcp是面向连接,传输可靠,用于传输大量数据,速度比较慢,建立连接需要的开销比较多
套接字类:QTcpSocket
//常用函数
abort(); //如果客户端与服务器已经连接,断开服务器,通常用在连接服务器前
connectToHost(); //连接服务器。参数:1.网络地址 2.端口号
disconnectFromHost(); //关闭连接
write(); //向外界发送数据。参数:发送的内容(const char*或QByteArray型)
readAll(); //读取外界传入的所有数据,返回值:QByteArray型
waitForConnected(); //等待与外界连接。参数:等待时间(毫秒单位),默认为3秒
waitForBytesWritten(); //写入多久,才执行后面的语句。参数:写入时间(毫秒单位),默认为3秒
waitForReadyRead(); //读取多久,才执行后面的语句。参数:读取时间(毫秒单位),默认为3秒
errorString(); //返回错误信息
isValid(); //判断套接字是否有效
isOpen(); //判断套接字是否打开
flush(); //刷新数据区
close(); //关闭套接字
//信号函数
connected(); //连接成功相应
disconnected(); //断开连接后相应
readyRead(); //有外界数据传入时相应
bytesWritten(qint64); //向外界发送数据成功后相应,参数为发送的字节数
服务器使用到的服务类:QTcpServer
//函数
isListening(); //判断是否处于监听状态
listen(); //监听。参数:1.服务端网络地址 2.服务端端口号
nextPendingConnection(); //外界有客户端连接后,得到套接字对象
close(); //关闭服务
//信号函数
newConnection(); //外界有客户端连接后响应
数据的传输以QByteArray类型的形式发送,介绍QString与QByteArray类型的转换
- QByteArray转QString:1.QString强转 2.QString静态方法fromLocal8Bit
- QString转QByteArray:1.QString函数:toUtf8(); 2.QString函数:toLocal8Bit()
一、客户端:Client
1.客户端与服务端的进行通信的主要步骤
- 通过服务器的网络地址(ip)和端口号与服务器连接(前提:服务器处于开启状态)
- 与服务器连接成功后,进行数据的传输与接收
- 关闭客户端
2.下列演示一个客户端
//客户端类
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
QTcpSocket* clientSocket;
bool isConnect;//一个标志,用于判断是否连接服务器
private slots:
void connectServerSlot();
void isConnectSlot();
void isDisConnectSlot();
void recvDataFromServerSlot();
void sendDataToServerSlot();
};
Widget::Widget(QWidget *parent) :
QWidget(parent),isConnect(false),//初始化连接表示为false
ui(new Ui::Widget)
{
ui->setupUi(this);
clientSocket=new QTcpSocket(this);
connect(ui->conServerBtn,SIGNAL(clicked()),this,SLOT(connectServerSlot()));//点击按钮连接/断开服务器
connect(clientSocket,SIGNAL(connected()),this,SLOT(isConnectSlot()));//连接成功后相应
connect(clientSocket,SIGNAL(disconnected()),this,SLOT(isDisConnectSlot()));//如果没有连接相应
connect(ui->sendMsgBtn,SIGNAL(clicked()),this,SLOT(sendDataToServerSlot()));//点击按钮发送消息
}
Widget::~Widget()
{
if(clientSocket->isOpen())
clientSocket->close();//关闭套接字
delete clientSocket;//释放套接字
delete ui;
}
void Widget::connectServerSlot()
{
if(!isConnect)//如果当前未连接
{
QString ServerIp=ui->serverIpLEdit->text();//得到服务器IP
QString Port=ui->portLEdit->text();//得到服务器端口号
clientSocket->connectToHost(QHostAddress(ServerIp),Port.toInt());//通过IP与端口号连接服务器
if(clientSocket->waitForConnected(1000))//如果等待一秒内连接成功
{
QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("连接服务器成功"));
isConnect=true;
}
else//如果连接超时
{
clientSocket->disconnect();//套接字不连接
QMessageBox::warning(this,QStringLiteral("提示"),QStringLiteral("连接服务器失败"));
}
}
else//如果当前连接
{
clientSocket->close();//关闭套接字
QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("断开服务器成功"));
isConnect=false;
}
}
void Widget::isDisConnectSlot()
{
ui->conServerBtn->setText(QStringLiteral("连接服务器"));
}
void Widget::isConnectSlot()
{
ui->conServerBtn->setText(QStringLiteral("断开服务器"));
//连接成功后,查看是否有数据发送过来
connect(clientSocket,SIGNAL(readyRead()),this,SLOT(recvDataFromServerSlot()));
}
void Widget::recvDataFromServerSlot()//接受数据
{
QByteArray recvData=clientSocket->readAll();//以字节类型得到数据
ui->recvTBrowser->append(QStringLiteral("服务器:")+QString::fromLocal8Bit(recvData));
ui->recvSizeTBrowser->setText(QString::number(recvData.size()));
}
void Widget::sendDataToServerSlot()//发送数据出去
{
QString sendData=ui->sendTEdit->toPlainText();
if(clientSocket->isOpen())//如果套接字处于打开状态
{
clientSocket->write(sendData.toLocal8Bit());//写数据
ui->recvTBrowser->append(QStringLiteral("客户端:")+sendData);
ui->sendSizeTBrowser->setText(QString::number(sendData.toLocal8Bit().size()));
ui->sendTEdit->clear();
}
}
二、服务器:Server
服务器与客户端的不同:服务器需要提供一个服务类,并用此类进行对外界的监听
1.客户端与服务端的进行通信的主要步骤
- 服务器开启服务,开启监听,监听外界是否有客户端连接
- 有客户端连接成功后,与客户端进行数据的传输
- 不需要时,关闭服务
2.下列演示一个服务端
//服务端类
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
QTcpServer* tcpServer;//服务
QTcpSocket* tcpServerSocket;//套接字
private:
void showHostAddress();
private slots:
void listenSlot();
void newConSlot();
void recvDataFromClientSlot();
void sendDataToClientSlot();
};
Widget::Widget(QWidget *parent) :
QWidget(parent),ui(new Ui::Widget)
{
ui->setupUi(this);
showHostAddress();//打印主机网络地址和端口
tcpServer=new QTcpServer(this);
connect(ui->listenBtn,SIGNAL(clicked()),this,SLOT(listenSlot())); //点击按钮开启加监听
connect(tcpServer,SIGNAL(newConnection()),this,SLOT(newConSlot()));//如果外界有客户端连接
connect(ui->sendMsgBtn,SIGNAL(clicked()),this,SLOT(sendDataToClientSlot()));//点击按钮发送数据
}
void Widget::showHostAddress()
{
//allAddresses:得到所有网络地址
QList<QHostAddress> addressList=QNetworkInterface::allAddresses();
foreach(QHostAddress address,addressList)//遍历网络地址
{
if(address.isNull())//为空退出
continue;
QAbstractSocket::NetworkLayerProtocol protocol=address.protocol();//得到网络地址类型
if(protocol!=QAbstractSocket::IPv4Protocol)//此处设置为判断是否为IPV4网络地址
continue;
ui->ipComBox->addItem(address.toString());//如果是IPV4,添加进组合框
}
}
void Widget::listenSlot()//监听
{
if(!tcpServer->isListening())//如果不处于监听状态,开启监听
{
//如果监听成功,参数:1.服务器IP地址 2.服务器端口号
if(tcpServer->listen(QHostAddress(ui->ipComBox->currentText()),\
ui->portLEdit->text().toInt()))
{
QMessageBox::information(this,QStringLiteral("提示"),QStringLiteral("启动监听成功"));
ui->listenBtn->setText(QStringLiteral("关闭监听"));
}
else
{
QMessageBox::warning(this,QStringLiteral("提示"),QStringLiteral("启动监听失败"));
}
}
else//如果处于监听状态,关闭监听
{
tcpServer->close();//关闭服务
tcpServerSocket->close();//关闭套接字
ui->clientTBrowser->clear();
ui->recvTBrowser->clear();
ui->recvSizeTBrowser->clear();
ui->sendSizeTBrowser->clear();
ui->listenBtn->setText(QStringLiteral("开启监听"));
}
}
void Widget::newConSlot()
{
tcpServerSocket=tcpServer->nextPendingConnection();//得到传入的客户套接字
QString clientName=tcpServerSocket->peerName();//得到客户端的名称
QHostAddress clientAddress=tcpServerSocket->peerAddress();//得到客户端的网络地址
qint64 clientPort=tcpServerSocket->peerPort();//得到客户端的端口
ui->clientTBrowser->append(QStringLiteral("客户端名:")+clientName+"\n");
ui->clientTBrowser->append(QStringLiteral("客户端IP:")+clientAddress.toString()+"\n");
ui->clientTBrowser->append(QStringLiteral("客户端端口:")+QString::number(clientPort)+"\n");
connect(tcpServerSocket,SIGNAL(readyRead()),this,SLOT(recvDataFromClientSlot()));//等待外界数据传入
}
void Widget::recvDataFromClientSlot()
{
QByteArray recvData=tcpServerSocket->readAll();//读取所有数据
ui->recvTBrowser->append(QStringLiteral("客户端:")+QString::fromLocal8Bit(recvData));
ui->recvSizeTBrowser->setText(QString::number(recvData.size()));
}
void Widget::sendDataToClientSlot()
{
QString sendData=ui->sendTEdit->toPlainText();
tcpServerSocket->write(sendData.toLocal8Bit());//向外发送数据
ui->recvTBrowser->append(QStringLiteral("服务端:")+sendData);
ui->sendSizeTBrowser->setText(QString::number(sendData.toLocal8Bit().size()));
ui->sendTEdit->clear();
}
三、运行结果
四、三次握手
服务端和客户端建立稳定的传输通道,需要3个步骤
- 客户端向服务器发送信息,发送一个报文(syn(请求同步))
- 服务端将受到的客户端的报文重新发回去,再多加一个报文(ack(确认))
- 客户端再向服务端发送报文(ack)
五、四次挥手
服务端和客户端断开传输通断,需要4个步骤
- 客户端向服务端发送报文(fin(结束标志))
- 服务端收到这个报文,它回复一个报文(ack)
- 服务端关闭客户端连接
- 客户端发挥(ack)报文确认