TCP 协议(Transmission Control Protocol)全称是传输控制协议是一种面向连接的、可靠的、
基于字节流的传输层通信协议。
tcp服务端使用QTcpServer、QTcpSocket。
tcp客户端使用QTcpSocket
1.在工程文件(工程文件.pro)中的第一行添加network 如
QT += core gui network //network是添加之后的
2.引用头文件
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字
![](https://img-blog.csdnimg.cn/img_convert/071635ca5993504a33c084af458adc67.png)
服务端
1.创建QTcpServer对象
2.启动服务器(监听)调用成员方法listen(QHostAddress::Any,端口号)
3.当有客户端连接时候会发送newConnection信号,触发槽函数接受连接(调用 nextPendingConnection函数得到一个与客户端通信的套接字QTcpSocket),通过QTcpSocket对象调用peerAddress、peerPort得到客户端的ip地址和端口。
4.QTcpsocket发送数据用成员方法write
5.读数据当客户端有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据。
和客户端建立连接之后用于通信的 QTcpSocket 套接字对象,它是 QTcpServer 的一个子对象,当 QTcpServer 对象析构的时候会自动析构这个子对象,当然也可自己手动析构,建议用完之后自己手动析构这个通信的 QTcpSocket 对象
超时接受连接函数waitForNewConnection
bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR);
msec:指定阻塞的最大时长,单位为毫秒(ms)
timeout:传出参数,如果操作超时 timeout 为 true,没有超时 timeout 为 false
常用信号
每次有新连接可用时都会发出 newConnection () 信号
当接受新连接导致错误时,将发射acceptError信号
常用的函数
virtual void disconnectFromHost(); //和对方断开连接
自己调用disconnectFromHost函数,则对方会收到信号void disconnected(),从而实现自己断开连接的处理,如关闭套接字。
onnect(tcpSocket,&QTcpSocket::disconnected,this,[=]()
{
//断开连接
tcpSocket->close();
tcpSocket->deleteLater();
emit recvOver();
});
virtual void incomingConnection(qintptr handle);
当有新的连接可用时,QTcpServer将调用这个虚拟函数。socketDescriptor参数是接受连接的本地套接字描述符。基本实现创建一个QTcpSocket,设置套接字描述符,然后将QTcpSocket存储在一个挂起连接的内部列表中。最后发出newConnection()。所以在服务器上一般说是incomingConnection和newConnection二选一实现。具体分别使用的场景:
如果服务器只和一个客户端连接,通讯的套接字和监听套接字在同一子线程,使用newConnection。
如果服务器和多个客户端连接,那需要一个客户端开一个线程,同时通讯套接字在每一个线程创建,把handle传到线程中,并在线程套接字调用setSocketDescriptor函数设置handle。QTcpServer若为每个客户端分配一个独立线程,必须重写incomingConnection()函数。
void someFunction()
{
//some code
//need that time cost process(ChatBusiness)
ChatBusiness *chatBusiness = new chatBusiness();
QThread *thread = new thread();
chatBusiness->moveToThread(thread);
emit chatBusiness->startSignal();//start the thread
}
void MyTcpServer::incomingConnection(qintptr handle)
{
//create subThreaad
QThread *thread = new QThread(this);
ChatBusiness *chatBusiness = new ChatBusiness();
chatBusiness->moveToThread(thread);
//def handle of start
connect(chatBusiness, &ChatBusiness::start, chatBusiness, &ChatBusiness::mainBusiness);
thread->start();
//send the SocketDescriptor
emit chatBusiness->start(handle);
}
void ChatBusiness::mainBusiness(qintptr handle)
{
QTcpSocket *tcpSocket = new QTcpSocket(this);
tcpSocket->setSocketDescriptor(handle);
}
QTcpServer类的工作机制是在有传入连接时,QTcpServer会创建一个与客户端匹配的socket,并返回一个指向socket内存的socketDescriptor(socket描述符),在QT中该描述符是qintptr类型的。然后QTcpServer会自动调用incomingConnection()函数,该函数接收这个socketDescriptor。
在incomingConnection()函数的原实现中,创建了一个QTcpSocket对象,然后调用函数QTcpSocket::setSocketDescriptor(qintptr socketDescriptor)设置socket描述符。最后在incomingConnection()函数中又调用了addPendingConnection(QTcpSocket * socket),将创建的QTcpSocket对象指针挂起在已创建列表中,该行为可终止waitForNewConnection()的阻塞,并且用户可以通过调用nextPendingConnection()函数获得这个QTcpSocket对象指针。在线程版的incomingConnection()函数中,可以省略这步addPendingConnection()的调用,因为不再需要通过nextPendingConnection()函数来获得socket指针了。
#ifndef CTCPSERVER_H
#define CTCPSERVER_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
class CTcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit CTcpServer(QTcpServer *parent = nullptr);
//发送信息到已连接的所有客户机
void sendData(QString data);
signals:
//通过该信号传递接收到的数据
void recvDataSignal(QString data);
public slots:
//当有新连接时的槽函数
void newClient();
//当有数据来时的槽函数
void readyReadData();
private:
QList<QTcpSocket *> m_clientList; //存放客户端socket的容器
};
#endif // CTCPSERVER_H
#include "CTcpServer.h"
#include <QTcpServer>
CTcpServer::CTcpServer(QTcpServer *parent) : QTcpServer(parent)
{
//设置可以连接的ip和端口号(此处为任意ip且端口号为6666的可以连接);若要指定ip,设置第一个参数即可
this->listen(QHostAddress::Any, 8866);
//连接相关的信号槽
connect(this, &CTcpServer::newConnection, this, &CTcpServer::newClient);
}
void CTcpServer::sendData(QString data)
{
//遍历客户端列表,将数据发送到所有客户端中(类似广播)
foreach (QTcpSocket *temp, m_clientList)
{
temp->write(data.toLocal8Bit(), data.length());
}
}
void CTcpServer::newClient()
{
//循环获取客户端socket
while (this->hasPendingConnections())
{
QTcpSocket *clientTemp = this->nextPendingConnection(); //拿到当前的socket
m_clientList.append(clientTemp); //将当前socket添加到列表中(方便操作)
connect(clientTemp, &QTcpSocket::readyRead, this, &CTcpServer::readyReadData);
}
}
void CTcpServer::readyReadData()
{
//拿到发送信号的客户端指针,通过该指针读取数据
QTcpSocket *curClient = dynamic_cast<QTcpSocket *>(sender());
emit recvDataSignal(curClient->readAll());
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
#include "ctcpserver.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_m_listenButton_clicked();
void on_m_sendButton_clicked();
void on_m_closeButton_clicked();
void on_m_exitButton_clicked();
void on_appendData(QString data); //将接收的数据显示
private:
Ui::MainWindow *ui;
CTcpServer *m_pserver;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_pserver(nullptr)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
if(m_pserver)
{
m_pserver->close();
delete m_pserver;
m_pserver = nullptr;
}
}
void MainWindow::on_m_listenButton_clicked()
{
m_pserver = new CTcpServer();
connect(m_pserver, &CTcpServer::recvDataSignal, this, &MainWindow::on_appendData);
}
void MainWindow::on_m_sendButton_clicked()
{
m_pserver->sendData(ui->m_sendlineEdit->text());
}
void MainWindow::on_m_closeButton_clicked()
{
if(m_pserver)
{
m_pserver->close();
delete m_pserver;
m_pserver = nullptr;
}
}
void MainWindow::on_m_exitButton_clicked()
{
}
void MainWindow::on_appendData(QString data)
{
ui->m_showtextBrowser->append(data);
}
客户端
1.创建QTcpSocket对象
2.链接服务器connectToHost(QHostAddress("ip"),端口号)
3.QTcpsocket发送数据用成员方法write
4.读数据当对方有数据来,QTcpSocket对象就会发送readyRead信号,关联槽函数读取数据
常用信号
readyRead
如果该类对象发射出 readyRead() 信号,说明对端发送的数据达到了,之后就可以调用 read 函数接收数据了。
connected
调用 connectToHost() 函数连接TCP服务器并成功建立连接之后发出 connected() 信号
disconnected
在套接字断开连接时发出 disconnected() 信号
void error(QAbstractSocket::SocketError);
一般用于调用connectToHost函数后,判断是否连接成功,如果连接不成功,会发射error信号,再通过SocketError error() const;判断是那种错误。
常用的函数
virtual void disconnectFromHost(); //和对方断开连接
自己调用disconnectFromHost函数,则对方会收到信号void disconnected(),从而实现自己断开连接的处理,如关闭套接字。见服务端的代码
SocketError error() const;
disconnectFromHost //断开连接
close //关闭连接
#ifndef CTCPSOCKET_H
#define CTCPSOCKET_H
#include <QObject>
#include <QTcpSocket>
class CTcpSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit CTcpSocket(QTcpSocket *parent = nullptr);
//通过改函数发送数据
void sendData(QString data);
signals:
//通过该信号传递接收到的数据
void recvDataSignal(QString data);
public slots:
//读取数据的槽函数
void readyReadData();
};
#endif // CTCPSOCKET_H
#include "CTcpSocket.h"
#include <QHostAddress>
CTcpSocket::CTcpSocket(QTcpSocket *parent) : QTcpSocket(parent)
{
//连接相应槽函数
connect(this, &CTcpSocket::readyRead, this, &CTcpSocket::readyReadData);
//指定ip且端口号为8866, (QHostAddress中指定的ip需本机存在或能连接到才可使用)
this->connectToHost(QHostAddress("127.0.0.1"), 8866);
}
void CTcpSocket::sendData(QString data)
{
this->write(data.toUtf8());
}
void CTcpSocket::readyReadData()
{
emit recvDataSignal(this->readAll());
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
#include "ctcpsocket.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_m_connectButton_clicked();
void on_m_sendButton_clicked();
void on_m_closeButton_clicked();
void on_appendData(QString data); //将接收的数据显示
private:
Ui::MainWindow *ui;
CTcpSocket *m_client;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
m_client(nullptr)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
if(m_client)
{
m_client->close();
delete m_client;
m_client = nullptr;
}
}
void MainWindow::on_m_connectButton_clicked()
{
m_client = new CTcpSocket;
connect(m_client, &CTcpSocket::recvDataSignal, this, &MainWindow::on_appendData);
}
void MainWindow::on_m_sendButton_clicked()
{
m_client->sendData(ui->m_sendlineEdit->text());
}
void MainWindow::on_m_closeButton_clicked()
{
if(m_client)
{
m_client->close();
delete m_client;
m_client = nullptr;
}
}
void MainWindow::on_appendData(QString data)
{
ui->m_showtextBrowser->append(data);
}