往期回顾
【QT进阶】Qt http编程之json解析的简单介绍-CSDN博客
【QT进阶】Qt http编程之nlohmann json库使用的简单介绍-CSDN博客
【QT进阶】Qt http编程之websocket的简单介绍-CSDN博客
【QT进阶】Qt http编程之实现websocket server服务器端
一、最终效果
通过ip地址和端口号,我可以开启服务去连接客户端并发送消息
二、套接字(补充一个知识点)
1、什么是套接字
套接字(Socket)是在计算机网络编程中用于实现网络通信的一种机制。它是网络通信的端点,可以用于在不同计算机之间或同一台计算机的不同进程之间进行数据传输。通过套接字编程,程序可以实现各种网络应用,实现计算机之间的数据交换和通信。
2、套接字类型
流套接字(StreamSocket):
基于TCP协议的套接字类型,提供面向连接的、可靠的、双向的数据流传输。适用于需要可靠传输和顺序数据传输的场景,如HTTP、FTP等应用。
数据报套接字(DatagramSocket):
基于UDP协议的套接字类型,提供无连接、不可靠的数据报传输。适用于需要快速传输但不需要可靠性的场景,如DNS、实时视频传输等。
3、套接字通信过程:
创建套接字: | 在应用程序中创建套接字对象,指定协议类型、地址族等参数。 |
绑定套接字: | 将套接字绑定到一个特定的IP地址和端口号,以便其他计算机或进程可以通过该地址与之通信。 |
监听连接: | 对于服务器端套接字,调用listen()函数开始监听客户端连接请求。 |
接受连接: | 对于服务器端套接字,调用accept()函数接受客户端的连接请求,返回一个新的套接字用于与客户端通信。 |
建立连接: | 对于客户端套接字,调用connect()函数向服务器发起连接请求,建立与服务器的连接。 |
数据传输: | 通过套接字的读写操作(如send()、recv())进行数据传输,实现通信功能。 |
关闭套接字: | 通信结束后,调用close()或类似函数关闭套接字,释放资源。 |
4、套接字编程:
在网络编程中,套接字通常由操作系统提供的网络库(如Socket API)来实现。
套接字编程可以使用不同的编程语言和平台实现,如C/C++、Python、Java等。
我们可以通过套接字编程实现各种网络通信应用,包括客户端-服务器模型、P2P通信、实时数据传输等。
三、基于Qt框架实现WebSocket服务器应用
在vs2019里创建,记得先包含QT模块:websockets,
右键单击项目->属性->Qt Project Settings->QT Moduls->选择websockets
1、主要功能实现
(1)通过Qt的QWebSocketServer类实现WebSocket服务器,用于处理客户端的连接和消息通信。
(2)可以开启、关闭服务器,监听指定的IP地址和端口,处理新的客户端连接,以及处理客户端断开连接事件。
(3)可以接收客户端发送的消息,并实现消息的转发功能。
2、整体思路:
2.1开启服务器
用户通过界面输入IP地址和端口号来开启WebSocket服务器。
WebsocketServerDemo::WebsocketServerDemo(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this->setWindowTitle(u8"Websocket Server");
this->resize(1000, 600);
ui.lineEdit_IP->setText("192.168.0.109"); //IP地址输入框
ui.lineEdit_Port->setText("8000");//端口号输入框
//创建websocketServer,用于处理WebSocket连接。
m_WebSocketServer = new QWebSocketServer(u8"server", QWebSocketServer::NonSecureMode);
}
2.2保存客户端信息
当有新的客户端连接时,将其添加到m_clients列表中,并保存对应的URL和套接字到mapSocket映射中,方便后续管理。
//处理新的链接,保存连接并更新在线用户列表。
void WebsocketServerDemo::onNewConnection()
{
//使用nextPendingConnection方法获取下一个挂起的连接
//返回一个QWebSocket指针pSocket,表示新连接的套接字
pSocket = m_WebSocketServer->nextPendingConnection();
//将新连接pSocket添加到m_clients列表中,用于跟踪所有客户端连接。
m_clients << pSocket;
//每个client上线时,链接他的textMessageReceived信号,处理接受的消息
connect(pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(processTextMessage(QString)));
//用户掉线处理
connect(pSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
//如何知道是哪个client发送的消息
//获取客户端连接的URL,用于标识客户端。
QString peerName = pSocket->requestUrl().toString();
cout << "peerName = " << peerName.toStdString() << endl;
//将客户端连接的URL和pSocket套接字保存到mapSocket映射中,用于后续管理和查找客户端连接。
mapSocket[peerName] = pSocket;
//将客户端连接的URL添加到在线用户列表listWidget_OnlineUser中,显示在线用户。
ui.listWidget_OnlineUser->addItem(peerName);
}
2.3连接槽函数
每个客户端连接都会关联处理消息接收和断开连接的槽函数。
//每个client上线时,链接他的textMessageReceived信号,处理接受的消息
connect(pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(processTextMessage(QString)));
//用户掉线处理
connect(pSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
2.4显示消息信息
在处理消息时,会显示时间、客户端信息和消息内容,并实现消息转发的功能。
//处理接收到的消息,显示在文本框中并处理消息转发。
void WebsocketServerDemo::processTextMessage(QString message)
{
QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");
QString item = pSocket->requestUrl().toString();
ui.textEdit_RecvMsg->append(time + "" + item + "\n" + message);
//处理消息转发
//...
}
2.5客户端断开连接时的处理
当客户端断开连接时,会从列表、映射中移除对应关系,并更新界面显示。
//客户端连接断开的操作,从在线用户列表中移除断开的客户端。
//处理客户端断开连接时的清理工作,包括从列表中移除客户端连接、从映射中移除对应关系,并更新界面显示。
void WebsocketServerDemo::socketDisconnected()
{
//智能索引遍历存储客户端连接的m_clients列表中的每个QWebSocket套接字sk
for (auto sk : m_clients)
{
if (!sk->isValid())//检查当前套接字sk是否有效,即客户端是否已断开连接
{
QString temp_key; //用于暂时存储要删除的客户端连接的URL。
ui.textEdit_RecvMsg->append("map size = " + QString(mapSocket.size()) + "\n");//显示当前mapSocket映射的大小。
//遍历mapSocket映射,查找与当前客户端套接字sk对应的URL
for (auto it = mapSocket.begin(); it!=mapSocket.end(); it++)
{
if (it.value() == sk)
{
//删除该客户端连接的相关信息:
//通过URL在在线用户列表中找到对应项,并删除。
QList<QListWidgetItem*> list;
list = ui.listWidget_OnlineUser-> findItems(it.key(), Qt::MatchCaseSensitive);
//从映射中移除该URL对应的套接字。
QListWidgetItem* sel = list[0];
int r = ui.listWidget_OnlineUser->row(sel);
QListWidgetItem* item = ui.listWidget_OnlineUser->takeItem(r);
ui.listWidget_OnlineUser->removeItemWidget(item);
delete item;
//从m_clients列表中移除当前客户端套接字sk。
m_clients.removeOne(sk);
QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");
ui.textEdit_RecvMsg->append(time + "" + it.key() + "下线了\n");
///将当前URL存储在temp_key中,用于后续删除操作。
temp_key = it.key();
}
}
//从mapSocket映射中移除存储在temp_key中的URL并显示移除后的mapSocket大小
mapSocket.remove(temp_key);
ui.textEdit_RecvMsg->append("after remove, map size = " + QString(mapSocket.size()) + "\n");
}
}
}
3、类结构分析
WebsocketServerDemo类是主要的应用类,继承自QWidget,负责处理界面相关的操作和WebSocket服务器的管理。
在构造函数中初始化界面和WebSocket服务器对象。
在析构函数中关闭WebSocket服务器。
包含槽函数:
on_btnOpenServer_clicked()用于开启服务器,
on_btnCloseServer_clicked()用于关闭服务器,
onNewConnection()处理新的连接,
processTextMessage()处理接收到的消息,
socketDisconnected()处理客户端断开连接,
on_btnSend_clicked()用于向所有客户端发送消息。
以上就是Qt里实现websocket server服务器端的简单介绍。
都看到这里了,点个赞再走呗朋友~
加油吧,预祝大家变得更强!