0 关于TCP通信
TCP:即传输控制协议,它提供了一种面向连接的、可靠的、字节流服务。
所谓 面向连接 指的是在数据交换之前必须先建立连接,然后实现点对点通信;
可靠性体现在它的丢包重发机制,另外数据被分割为数据块发送,接收时要通过首部与数据的校验和;
字节流体现在TCP发送、接收存在缓冲区,故通信双方读写操作次数可以不同.
关于更多TCP的内容比如建立、断开连接,报文段等内容,网上有很多教程,本文着重讲解Qt下TCP通信过程。
一、Qt下TCP通信流程
TCP中通信双方为别为客户端和服务器,Qt中为TCP通信提供了两个类QTcpServer
和QTcpSocket
,分别为监听套接字(监听有无连接请求,用于服务器端)和通信套接字(发送、读取数据)。使用时,应在项目文件中添加QT += network
,然后再添加相应的头文件。
首先介绍服务器端
- 由于服务器端既要监听有无客户端的连接请求,又要发送和接收数据,故需要创建
监听套接字对象
和通信套接字对象
; - 然后监听套接字
开始监听
IP地址和端口是否有连接请求:
其中,tcpServer->listen(QHostAddrss::Any,8888);
QTcpServer::listen(QHostAddress adr,quint16 port)
即为监听函数,它的两个参数分别为绑定的IP地址和端口号,该函数有bool类型返回值,返回true
表明监听建立成功,返回false
表明监听建立失败; - 监听建立成功后,当有新的连接发起时,会触发服务器
newConnection()
信号,所以要定义一个槽函数对请求连接的客户端进行操作,并将该槽函数与newConnection()
进行连接:connect(TcpServer,SIGNAL(newConnection()),this,SLOT(mconnect()));
- 在槽函数中取出客户端的通信套接字,如果客户端有数据发送过来,则会触发通信套接字的
readyRead()
信号,故需要顶一个槽函数来读取数据,并将该槽函数与readyRead()
连接:
其中void Widget::mconnect() { qDebug()<<"connect sucess"; TcpSocket = TcpServer->nextPendingConnection(); connect(TcpSocket,SIGNAL(readyRead()),this,SLOT(RcvData())); QString ip = TcpSocket->peerAddress().toString(); quint16 port = TcpSocket->peerPort(); ui->textEdit_read->setText(QString("[%1:%2]:成功连接").arg(ip).arg(port)); }
TcpSocket = TcpServer->nextPendingConnection();
需要注意一下,由于服务器可能接收到多个客户端的连接请求,所以有新连接发起时,系统会将新连接放入队列PendingConnection
中,故取出通信套接字时秩序调用TcpServer->nextPendingConnection()
,便会返回下一个可用的通信套接字。
另外,QTcpServer
还提供了几个函数对该队列进行操作:bool hasPendingConnections(); //是否有可用新连接 int maxPendingConnections(); //返回队列的最大值 void setMaxPendingConnections(int numConnections); //设置队列最大值 QTcpSocket *nextPendingConnection(); //返回下一个可用的通信套接字
- 在
RcvData()
中读取数据并显示:
这样,服务器接收数据便完成了,发送数据很简单:void Widget::RcvData() { QString msg = TcpSocket->readAll(); ui->textEdit_read->setText(msg); }
- 发送数据:
void Widget::on_pushButton_send_clicked() { QString str = ui->textEdit_write->toPlainText(); TcpSocket->write(str.toUtf8().data()); }
接下来是客户端
- 相比较服务器而言,客户端由于只有发起连接请求与数据收发的功能,而无需监听,所以只需要创建
通信套接字对象
; - 首先是发起连接请求 ,
QTcpSocket
提供了connectToHost(QHostAddress adr,quint16 port)
函数,用于向指定的Ip地址和端口发起连接请求:
void Client::on_pushButton_connect_clicked()
{
socket->abort(); //发起连接之前先断开原来的连接
QString ip = ui->lineEdit_ip->text();
quint16 port = ui->lineEdit_port->text().toInt();
qDebug()<<ip<<port;
socket->connectToHost(QHostAddress(ip),port);
}
- 同样,发送数据只需用到
QtcpSocket::write(QByteArry data)
函数:void Client::on_pushButton_send_clicked() { QString txt = ui->textEdit_write->toPlainText(); socket->write(txt.toUtf8().data()); }
- 接收数据放在
newConnection()
对应的槽函数中:void Client::recivedata() { QString datas=socket->readAll(); ui->textEdit_read->setText(datas); }
二、程序源码
项目结构:
ui界面–Widget:
ui界面–Client:
client.h:
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class Client;
}
class Client : public QWidget
{
Q_OBJECT
public:
explicit Client(QWidget *parent = 0);
~Client();
private slots:
void displayError(QAbstractSocket::SocketError);
void on_pushButton_connect_clicked();
void recivedata();
void on_pushButton_send_clicked();
private:
Ui::Client *ui;
QTcpSocket *socket;
};
#endif // CLIENT_H
client.cpp:
#include "client.h"
#include "ui_client.h"
#include <QHostAddress>
#include <QDebug>
Client::Client(QWidget *parent) :
QWidget(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
socket = new QTcpSocket(this);
connect(socket,SIGNAL(readyRead()),this,SLOT(recivedata()));
connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),
this,SLOT(displayError(QAbstractSocket::SocketError)));
}
Client::~Client()
{
delete ui;
}
void Client::on_pushButton_connect_clicked()
{
socket->abort();
QString ip = ui->lineEdit_ip->text();
quint16 port = ui->lineEdit_port->text().toInt();
qDebug()<<ip<<port;
socket->connectToHost(QHostAddress(ip),port);
}
void Client::displayError(QAbstractSocket::SocketError)
{
qDebug()<<socket->errorString();
socket->close();
}
void Client::recivedata()
{
QString datas=socket->readAll();
ui->textEdit_read->setText(datas);
}
void Client::on_pushButton_send_clicked()
{
QString txt = ui->textEdit_write->toPlainText();
socket->write(txt.toUtf8().data());
}
widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void init();
void newListen();
void mconnect();
void RcvData();
void on_pushButton_send_clicked();
void on_pushButton_close_clicked();
void displayError(QAbstractSocket::SocketError);
private:
Ui::Widget *ui;
QTcpServer *TcpServer;
QTcpSocket *TcpSocket;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QList>
#include <QNetworkInterface>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
init();
}
Widget::~Widget()
{
delete ui;
}
void Widget::init()
{
TcpServer = new QTcpServer(this);
TcpSocket = new QTcpSocket(this);
newListen();
connect(TcpServer,SIGNAL(newConnection()),this,SLOT(mconnect()));
connect(TcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
this,SLOT(displayError(QAbstractSocket::SocketError)));
}
void Widget::newListen()
{
if(!TcpServer->listen(QHostAddress::Any,8000))
{
qDebug()<<TcpServer->errorString();
close();
return;
}
}
void Widget::on_pushButton_send_clicked()
{
QString str = ui->textEdit_write->toPlainText();
TcpSocket->write(str.toUtf8().data());
}
void Widget::on_pushButton_close_clicked()
{
TcpSocket->disconnectFromHost();
TcpSocket->close();
}
void Widget::mconnect()
{
qDebug()<<"connect";
TcpSocket = TcpServer->nextPendingConnection();
connect(TcpSocket,SIGNAL(readyRead()),this,SLOT(RcvData()));
QString ip = TcpSocket->peerAddress().toString();
quint16 port = TcpSocket->peerPort();
ui->textEdit_read->setText(QString("[%1:%2]:成功连接").arg(ip).arg(port));
}
void Widget::displayError(QAbstractSocket::SocketError)
{
qDebug()<<TcpSocket->errorString();
TcpSocket->close();
}
void Widget::RcvData()
{
QString msg = TcpSocket->readAll();
ui->textEdit_read->setText(msg);
}
main.cpp
#include "widget.h"
#include <QApplication>
#include "client.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
Client wr;
wr.show();
w.show();
return a.exec();
}