在【P2P学习(2)】P2P 通信,主要存在四种不同的网络模型的第一阶段:集中式P2P 模式
最简单的路由方式就是集中式,即存在一个中心节点保存了其他所有节点的索引信息,索引信息一般包括节点 IP 地址、端口、节点资源等。集中式路由的优点就是结构简单、实现容易。但缺点也很明显,由于中心节点需要存储所有节点的路由信息,当节点规模扩展时,就很容易出现性能瓶颈;而且也存在单点故障问题。
一般是有一个服务器,和一群客户端。各个客户端可以两两相互发送消息。各个客户端用IP地址和TCP/IP监听端口号进行标识。客户端,可执行注册和聊天过程;注册服务器,主要用于注册客户端和分发注册客户端信息。
技术点:
认识Qt5::Network
Qt中提供的所有的Socket类都是非阻塞的。
QTcpSocket 用于TCP/IP通信,作为客户端套接字使用。
QTcpServer 用于TCP/IP通信,作为服务器端套接字使用。
QUdpSocket 用于UDP通信,服务器,客户端均使用此套接字。
文档资料:
官方文档: Qt Network C++ Classes:https://doc.qt.io/qt-5/qtnetwork-module.html
C++ QTcpSocket::waitForConnected方法代码示例:https://vimsky.com/examples/detail/cpp-ex—QTcpSocket-waitForConnected-method.html
CMake下添加,在CMakelists.txt文件中添加以下代码
#设置Qt的支持
set(QT5_LIBRARIESQt5::Network)
#自动查找配置构建工程所需的程序库
find_package(Qt5Network REQUIRED)
#添加可执行文件所需要的库
target_link_libraries(${PROJECT_NAME} ${QT5_LIBRARIES} Qt5::Network)
然后,引入相关类的头文件:
#include
#include
#include
TCP/IP: TCP(Transmission Control Protocol,传输控制协议),IP(Internet Protocol 网络协议)。TCP提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量,是一种流控制。认识TCP可通过以下方面。
Socket又称"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
服务器监听客户端流程
1.创建套接字QTcpServer
2.监听指定的地址和端口号(周期性线程调用)
3.监听是否有Client连接
4.停止监听close
5.通过监听到的QTcpSocket收发数据
6.获取服务器监听状态
创建套接字
QTcpServer* server = new QTcpServer();
监听指定的地址和端口号,即进入Sever启动监听等待Client连接状态
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any,
quint16 port = 0);
监听是否有Client连接
QTcpSocket* QTcpServer::nextPendingConnection();
注意,返回的QTcpSocket只能再本线程使用;如果需要在别的线程管理该QTcpSocket,则需要重写QTcpServer::incomingConnection(),将QTcpSocket描述符传递给其他线程并再创建QTcpSocket。
void QTcpServer::incomingConnection(qintptr handle);
停止监听
void QTcpServer::close();
收发数据
根据第3不中返回的QTcpSocket指针,调用read()和write()接口。
其他辅助接口
获取己方和对方的IP和port
quint16 QTcpServer::serverPort() const;
QHostAddress QTcpServer::serverAddress() const;
获取服务器监听状态
bool QTcpServer::isListening() const;
ServerListen 断开重连机制
static int count = 0;
void UserServerlient::Listen() {
//1.tcpServer的断开重连
if (!tcpClient) { return; }
if (isListening() != lastState) {
count++;
//服务器尝试重新监听某个地址和端口
ServerListening();
//判断当前服务器监听状态
if (isListening()) {
std::cout << localIP << "/" << localPort << " 断开重连count: " << count;
count = 0;
}
}
//数字10为断开重连次数,根据设置的线程周期时间和实际需求设置
if (count >= 10) {
lastState = false;
delete this;
count = 0;
return;
}
//2.检测连接到的Client
if (!child_client) {
child_client = server->nextPendingConnection(); //得到每个连进来的socket
if (!child_client) {return;}
}
//检测到Client断开连接,清理内存
if (child_client->state() == QAbstractSocket::UnconnectedState) {
……
}
}
客户端通信流程
1.创建套接字QTcpSocket
2.连接服务器,使用 QTcpSocket::connectToHost()
3.向服务器发送数据 QTcpSocket::write()
4.接收服务器数据 QTcpSocket::readAll()
5.断开Socket连接等接口
6.获取己方和对方的IP和port,Socket状态等;
创建套接字
QTcpSocket* socket = new QTcpSocket();
客户端连接到服务器端
// ### Qt6: de-virtualize connectToHost(QHostAddress) overload
void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
OpenMode mode = ReadWrite,
NetworkLayerProtocol protocol = AnyIPProtocol);
void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port,
OpenMode mode = ReadWrite);
连接成功或断开服务器端,或触发connected()和disconnected()信号
Q_SIGNALS:
void QAbstractSocket::hostFound();
void QAbstractSocket::connected();
void QAbstractSocket::disconnected();
void QAbstractSocket::stateChanged(QAbstractSocket::SocketState);
连接成功后发送数据。返回值:数据信息的长度。
qint64 QIODevice::write(const char *data, qint64 len);
qint64 QIODevice::write(const char *data);
qint64 QIODevice::write(const QByteArray &data);
连接成功接收数据。返回值:数据信息info
qint64 QIODevice::read(char *data, qint64 maxlen);
QByteArray QIODevice::read(qint64 maxlen);
QByteArray QIODevice::readAll();
当有新的数据到来时,会触发readyRead()信号。因此,要时刻检测数据有数据发送过来,此处应该放在一个循环中,如timer中。
Q_SIGNALS:
void QIODevice::readyRead();
void QIODevice::aboutToClose();
关闭TCP连接
//会等待数据被写完之后关闭套接字连接
void QAbstractSocket::disconnectFromHost();
//关闭套接字的I/O设备并调用disconnectFromHost()来关闭套接字的连接。
void QIODevice::close() override;
//调用close(),终止当前连接并重置套接字。与disconnectFromHost()不同,这个函数会立即关闭套接字,丢弃写缓冲区中任何未处理的数据。
void QAbstractSocket::abort();
其他辅助接口
获取己方和对方的IP和port
quint16 QAbstractSocket::localPort() const;
quint16 QAbstractSocket::peerPort() const;
QHostAddress QAbstractSocket::localAddress() const;
QHostAddress QAbstractSocket::peerAddress() const;
QString QAbstractSocket::peerName() const;
获取Socket状态
SocketState QAbstractSocket::state();
enum QAbstractSocket::SocketState {
UnconnectedState, //未连接状态
ConnectedState, //连接状态
};
ClientListen 断开重连机制
static int count = 0;
void UserSocketlient::Listen() {
if (!tcpClient) { return; }
if (CurrentSocketConnectState() != lastState) {
count++;
//客服端尝试连接服务器
SocketConnecttoSever();
//判断当前Socket连接状态
if (CurrentSocketConnectState()) {
std::cout << localIP << "/" << localPort << " 断开重连count: " << count;
count = 0;
}
}
//数字10为断开重连次数,根据设置的线程周期时间和实际需求设置
if (count >= 10) {
lastState = false;
delete this;
count = 0;
return;
}
}
用途:
1、接受客户端的加盟,然后记录客户端的列表信息
;
2、分发列表信息
的端口信息给全体客户端,这样客户端就可相互联系了,不需要中心服务器的支持。
3、实时更新列表,有些下线的,超时的用户踢出去,然后再次广播列表。
【QT开发(13)】QT发布到其他ubuntu用户
https://gitee.com/hiyanyx/p2-pcha
链接:https://blog.csdn.net/qq_43572400/article/details/128261772