网络营销项目策划书,惠州谷歌优化,番禺网站开发哪家强,长沙做公司网站目录
一、编写思路
1、服务器
#xff08;1#xff09;总体思路widget.c#xff08;主线程#xff09;
#xff08;2#xff09;详细流程widget.c#xff08;主线程#xff09;
#xff08;1#xff09;总体思路chat_thread.c#xff08;处理聊天逻辑线程…目录
一、编写思路
1、服务器
1总体思路widget.c主线程
2详细流程widget.c主线程
1总体思路chat_thread.c处理聊天逻辑线程
2详细流程chat_thread.h处理聊天逻辑线程
2、客户端
1总体思路widget.c主线程
2详细思路widget.c主线程
1总体思路chat_thread.c(处理聊天逻辑线程)
2详细流程chat_thread.c(处理聊天逻辑线程)
二、实现效果
1、服务器
2、客户端 完整代码请到指定链接下载Qt网络编程-Tcp多线程并发服务器和客户端通信: 【Qt网络编程】Tcp多线程并发服务器和客户端通信
一、编写思路
1、服务器
1总体思路widget.c主线程 初始化界面 创建窗口、输入框、按钮等基本UI元素。 创建服务器对象 实现 My_tcp_server 并监听客户端连接。 处理新客户端连接 当有新客户端连接时创建新的 Chat_thread 线程来处理通信。 绑定信号槽 确保主线程与客户端处理线程间的信号槽连接使用 Qt::QueuedConnection 处理跨线程通信。 处理消息传递 接收和发送消息并在界面上更新显示。 服务器启动与关闭 通过按钮控制服务器的启动和关闭管理所有客户端线程的安全退出。 2详细流程widget.c主线程 创建 Qt 界面及设置窗口属性 首先通过 ui-setupUi(this); 来初始化用户界面并设置窗口标题、大小等基本属性。这是 Qt 项目的常见步骤通过 .ui 文件生成的类进行界面管理。 Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) // 初始化UI对象
{ui-setupUi(this); // 设置UI界面this-setWindowTitle(--服务器--); // 设置窗口标题this-resize(1024, 960); // 设置窗口大小
ui-le_ip-setText(127.0.0.1);ui-le_port-setText(9999);
} 初始化服务器对象 My_tcp_server 并处理客户端连接 创建 tcp_server 对象以处理客户端的连接。 使用 connect 函数连接 tcp_server 的 new_descriptor 信号和匿名槽函数确保一旦有新客户端连接便创建一个 Chat_thread 来处理该客户端。 this-tcp_server new My_tcp_server(this);
connect(tcp_server, My_tcp_server::new_descriptor, this, [](qintptr socketDescriptor){QMessageBox::information(this, 提示, 新的客户端连接, QMessageBox::Ok, QMessageBox::Information);
ui-btn_send-setEnabled(true); // 启用“发送消息”按钮
// 创建新线程处理客户端Chat_thread *chat_thread new Chat_thread(socketDescriptor);chat_thread-moveToThread(chat_thread); // 将线程和对象绑定到同一线程防止冲突
thread_list.append(chat_thread);// 启动线程处理客户端通信chat_thread-start();
}); 管理客户端线程 Chat_thread 每当有新客户端连接时创建一个 Chat_thread 并启动它处理客户端通信。通过 moveToThread 将 Chat_thread 的执行线程与该对象保持一致避免跨线程冲突。 使用 connect 绑定线程中的信号如连接断开、接收消息和主界面槽函数确保客户端状态能够正确显示。 Chat_thread *chat_thread new Chat_thread(socketDescriptor);
chat_thread-moveToThread(chat_thread); // 将线程与对象绑定在同一线程
thread_list.append(chat_thread);
// 连接信号和槽
connect(chat_thread, Chat_thread::break_connect, this, [](){ui-te_receive-append(currentTime \n【状态】客户端断开连接...);ui-btn_send-setEnabled(false); // 禁用“发送消息”按钮
});
connect(chat_thread, Chat_thread::recv_info, this, [](QString data){currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime form client\n 【数据】 data); // 在文本框中显示消息
});
chat_thread-start(); // 启动线程 处理启动和关闭服务器的按钮事件 on_btn_connect_clicked() 处理连接按钮点击事件启动或关闭服务器。 启动时检查 IP 地址和端口的有效性成功后开始监听客户端连接。 关闭服务器时停止监听并确保所有已连接客户端线程安全退出。 void Widget::on_btn_connect_clicked()
{if (!is_server_running){// 启动服务器QString ip_address ui-le_ip-text().trimmed();QString port_text ui-le_port-text().trimmed();if (!tcp_server-listen(QHostAddress(ip_address), port_text.toUInt())){QMessageBox::warning(this, warning, 服务器监听失败);return;}is_server_running true;ui-btn_connect-setText(关闭服务器);ui-te_receive-append(currentTime \n【状态】服务器开始监听...);}else{// 停止服务器并关闭所有客户端线程tcp_server-close();for (Chat_thread *thread : qAsConst(thread_list)){thread-exit();thread-wait();thread-deleteLater();}thread_list.clear();is_server_running false;ui-btn_connect-setText(创建服务器);ui-te_receive-append(currentTime \n【状态】服务器已停止监听...);}} 处理发送消息按钮的点击事件 当点击“发送消息”按钮时触发 send_request 信号利用信号槽机制将输入的消息发送给客户端。需要确保主线程和子线程的信号槽通信是异步进行的通过 Qt::QueuedConnection。 void Widget::on_btn_send_clicked()
{QString data ui-te_send-toPlainText().toUtf8();emit send_request(data); // 发出 send_request 信号
} 服务器监听客户端的状态和信息传递 服务器通过 recv_info 和 send_info 信号接收客户端消息并在界面上显示。 在客户端连接成功或断开时更新界面显示状态。 connect(chat_thread, Chat_thread::recv_info, this, [](QString data){currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime form client\n 【数据】 data); // 显示接收的客户端数据
});
connect(chat_thread, Chat_thread::send_info, this, [](QString data){currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime to client\n 【数据】 data); // 显示发送给客户端的数据
});
1总体思路chat_thread.c处理聊天逻辑线程 构造函数 初始化 socketDescriptor 以供后续线程使用。 线程启动与套接字初始化 在 run() 函数中创建 QTcpSocket并关联 socketDescriptor。 获取客户端信息 通过 peerAddress() 和 peerPort() 获取客户端 IP 地址和端口号并进行错误处理。 信号槽机制连接 将套接字状态、接收数据、错误处理等信号连接到相应的槽函数。 处理连接状态变化 通过 handler_client_changed() 处理客户端的连接或断开并发出相应的信号。 处理接收消息 在 receive_message() 函数中处理客户端发送的消息并发出信号 recv_info。 发送消息 在 send_message() 函数中检查连接状态并发送消息发出 send_info 信号。 错误处理 处理客户端连接中的错误删除资源并退出线程。 2详细流程chat_thread.h处理聊天逻辑线程 构造函数初始化 Chat_thread 构造函数接受一个 socketDescriptor 参数并将其存储为类的成员变量以供 run() 函数中使用。注意QTcpSocket 对象将在 run() 函数中创建以确保在新线程中创建并使用。 Chat_thread::Chat_thread(qintptr socketDescriptor, QObject *parent): QThread{parent}, socketDescriptor(socketDescriptor)
{// socketDescriptor 存储为成员变量
} 线程启动和套接字初始化 在 run() 函数中创建 QTcpSocket 对象并通过 setSocketDescriptor() 将套接字描述符与 QTcpSocket 关联。这允许线程使用此套接字与客户端通信。 如果套接字初始化失败进行错误处理并返回。 void Chat_thread::run()
{// 创建 QTcpSocket 对象用于处理与客户端的通信this-socket new QTcpSocket();
// 将套接字描述符与 QTcpSocket 关联if (!socket-setSocketDescriptor(socketDescriptor)){qDebug() Error: Failed to get new socketDescriptor.;return;}
// 错误处理检查是否成功获取客户端连接if (socket nullptr){qDebug() Error: Failed to get new client connection.;return; // 如果获取失败直接返回}
} 获取客户端信息 在成功创建套接字后获取客户端的 IP 地址和端口号。 如果获取失败进行错误处理并断开连接。 // 获取客户端的IP地址和端口号
QString ip_addr socket-peerAddress().toString();
quint16 port socket-peerPort();
// 错误处理检查是否成功获取IP地址和端口号
if (ip_addr.isEmpty() || port 0)
{qDebug() Error: Failed to get clients IP address or port.;socket-disconnectFromHost(); // 断开连接socket-deleteLater(); // 删除客户端套接字对象return; // 如果获取失败直接返回
} 信号槽机制的连接 连接套接字的状态改变信号 stateChanged 到槽函数 handler_client_changed以便监控客户端连接状态的变化。 连接 QTcpSocket 的 readyRead 信号到 receive_message 槽函数用于处理接收数据。 处理套接字错误时连接 errorOccurred 信号到 handle_socket_error 槽函数。 // 处理连接状态变化的槽函数
connect(socket, QTcpSocket::stateChanged, this, Chat_thread::handler_client_changed);
// 错误处理处理客户端的异常断开情况
connect(socket, QTcpSocket::errorOccurred, this, Chat_thread::handle_socket_error);
// 处理接收数据的槽函数
connect(socket, QTcpSocket::readyRead, this, Chat_thread::receive_message); 处理客户端连接状态变化 在 handler_client_changed() 槽函数中根据客户端的连接状态如断开、已连接做相应处理并发出信号通知其他部分更新状态。 void Chat_thread::handler_client_changed(QAbstractSocket::SocketState socket_state)
{socket (QTcpSocket*)sender(); // 获取发信的客户端套接字if(!socket) return;
switch (socket_state){case QAbstractSocket::UnconnectedState: // 客户端断开连接emit break_connect();break;
case QAbstractSocket::ConnectedState: // 客户端已连接emit complete_connect();break;
default:break;}
} 接收消息的处理 在 receive_message() 槽函数中通过 socket-readAll() 读取客户端发送的所有数据并发出信号 recv_info 通知上层处理。 void Chat_thread::receive_message()
{if (socket){QString data socket-readAll(); // 读取客户端发送的所有数据emit recv_info(data); // 发出信号通知收到消息}
} 发送消息 在 send_message() 函数中检查客户端是否处于连接状态如果是则发送消息否则输出警告信息。 发送完成后发出 send_info 信号。 void Chat_thread::send_message(QString data)
{if (socket-state() QAbstractSocket::ConnectedState){socket-write(data.toUtf8()); // 发送数据}else{qDebug() warning: 客户端未连接无法发送消息; // 输出警告}
emit send_info(data); // 发出信号通知发送消息
} 错误处理 在 handle_socket_error() 函数中处理 QTcpSocket 的错误。如果出现错误打印错误信息并退出线程。 删除套接字对象并退出线程事件循环。 void Chat_thread::handle_socket_error(QAbstractSocket::SocketError socketError)
{qDebug() Client connection error, error code: socketError;
this-exit(); // 退出线程this-wait(); // 等待线程完全退出socket-deleteLater(); // 删除客户端套接字对象
// 停止线程事件循环quit();
}
2、客户端
1总体思路widget.c主线程 初始化界面 设置窗口属性并初始化用户输入的默认值。 创建线程和通信任务对象 实现异步通信使用 QThread 和自定义 Chat_thread 处理服务器交互。 信号槽机制的建立 连接 UI 和工作线程之间的信号槽确保各操作异步处理。 线程管理 在析构函数中确保线程安全退出释放资源。 处理连接与断开 通过按钮触发连接和断开操作并更新 UI 显示。 消息传递与显示 处理消息的发送与接收并在 UI 界面上更新显示结果。 2详细思路widget.c主线程 初始化界面 使用 ui-setupUi(this) 初始化用户界面并设置窗口标题和窗口大小。 初始化 IP 地址和端口号的默认值。 Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), is_connected(false) // 初始化连接状态为未连接
{ui-setupUi(this); // 设置UI界面this-setWindowTitle(-客户端-); // 设置窗口标题this-resize(1024, 960); // 设置窗口大小
ui-le_ip-setText(127.0.0.1); // 设置默认IP地址ui-le_port-setText(8888); // 设置默认端口号
} 创建线程和通信任务对象 创建 QThread 对象以进行异步通信任务。 创建 Chat_thread 对象负责与服务器进行通信操作。 使用 moveToThread 将通信任务对象移到新的线程中执行并启动该线程。 // 创建线程对象
thread new QThread;
// 创建任务对象负责与服务器的通信
Chat_thread *worker new Chat_thread;
worker-moveToThread(thread); // 将任务对象移至线程
thread-start(); // 启动工作线程 信号槽机制的建立 使用信号槽连接 UI 和工作线程之间的交互。例如连接服务器、发送消息、断开连接等操作通过信号槽机制进行。 信号从 UI 线程发出工作线程的槽函数接收信号并执行相关操作。 // 信号槽连接从UI线程发出连接信号worker线程接收并执行连接操作
connect(this, Widget::connect_server, worker, Chat_thread::start_connected);
connect(this, Widget::send_info, worker, Chat_thread::start_send);
connect(this, Widget::quit_connect, worker, Chat_thread::break_connected);
// 连接断开信号槽worker线程通知UI线程更新UI
connect(worker, Chat_thread::connect_cancel, this, Widget::submit_connect_cancel);
connect(worker, Chat_thread::connected, this, Widget::submit_connect_info);
connect(worker, Chat_thread::transfer_recv_info, this, Widget::submit_recv_info); 管理线程的生命周期 在析构函数中确保工作线程在窗口关闭时被正确停止并释放相关资源。 如果线程正在运行需要先请求线程退出然后等待其完全退出后再删除。 Widget::~Widget()
{if (thread-isRunning()){thread-quit(); // 请求线程退出thread-wait(); // 等待线程结束}delete worker; // 删除任务对象delete thread; // 删除线程对象delete ui; // 删除UI对象
} 处理连接成功或断开连接的槽函数 当客户端成功连接到服务器时工作线程发出 connected 信号UI 界面通过槽函数 submit_connect_info() 来更新显示状态并启用“发送消息”按钮。 断开连接时UI 界面通过槽函数 submit_connect_cancel() 来禁用“发送消息”按钮并更新状态显示。 // 连接成功时的槽函数更新UI显示信息
void Widget::submit_connect_info()
{currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime \n【状态】已成功连接到服务器);ui-btn_connect-setText(断开服务器);ui-btn_send-setEnabled(true); // 启用发送按钮is_connected true;
}
// 断开连接时的槽函数更新UI显示信息
void Widget::submit_connect_cancel()
{is_connected false;
} 处理消息的发送与接收 当用户点击“发送消息”按钮时获取文本框中的消息发出 send_info 信号将消息发送到服务器。 当从服务器接收到消息时工作线程发出 transfer_recv_info 信号UI 界面更新显示接收到的消息。 // 当用户点击发送按钮时读取输入框中的内容并发送给服务器
void Widget::on_btn_send_clicked()
{currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);QString message ui-te_send-toPlainText().toUtf8(); // 获取用户输入的消息ui-te_receive-append(currentTime to server\n 【数据】 message \n);emit send_info(message); // 发出信号通知工作线程发送消息
}
// 当接收到服务器发送的消息时更新UI显示接收到的消息
void Widget::submit_recv_info(QString message)
{currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime form server\n 【数据】 message \n); // 显示服务器的消息
} 处理连接与断开的按钮事件 当点击“连接”按钮时获取 IP 地址和端口号检查输入的有效性后发出 connect_server 信号通知工作线程与服务器建立连接。 当点击“断开服务器”按钮时发出 quit_connect 信号通知工作线程断开连接。 // 当用户点击连接按钮时触发该槽函数
void Widget::on_btn_connect_clicked()
{if (!is_connected){QString ip_address ui-le_ip-text().trimmed();QString port_text ui-le_port-text().trimmed();
QHostAddress address;if (!address.setAddress(ip_address)) // 检查IP地址的有效性{QMessageBox::warning(this, warning, 无效的IP地址请重新输入);return;}
bool ok;unsigned int port port_text.toUInt(ok);if (!ok || port 0 || port 65535) // 检查端口号的有效性{QMessageBox::warning(this, warning, 无效的端口号请输入1到65535之间的数值);return;}
emit connect_server(ip_address, port); // 发出连接服务器的信号}else{currentTime QDateTime::currentDateTime().toString(yyyy-MM-dd hh:mm:ss);ui-te_receive-append(currentTime \n【状态】已断开与服务器的连接);ui-btn_send-setEnabled(false); // 禁用发送按钮is_connected false;emit quit_connect(); // 发出断开连接的信号}
}
1总体思路chat_thread.c(处理聊天逻辑线程) 构造函数 初始化 Chat_thread 对象。 接收消息 通过 readyRead 信号槽接收服务器发送的数据并将其转发给主线程。 处理连接状态变化 监控与服务器的连接状态并打印调试信息。 断开连接 关闭套接字连接并释放资源发出连接断开信号。 启动连接 通过指定的 IP 和端口号连接服务器并处理连接成功、失败、断开、接收数据等事件。 发送消息 检查连接状态并发送消息。如果未连接则发出未连接信号。 2详细流程chat_thread.c(处理聊天逻辑线程) 构造函数 构造函数 Chat_thread::Chat_thread(QObject *parent) 初始化 Chat_thread 对象。在这个阶段不需要任何复杂的逻辑主要是确保对象正常创建。 Chat_thread::Chat_thread(QObject *parent): QObject{parent}
{} 接收消息处理 receive_message() 是一个槽函数用于接收从服务器发送的数据。当 QTcpSocket 对象有数据可读取时信号 readyRead 会被触发调用此槽函数。读取数据后通过 transfer_recv_info 信号将接收到的消息发送出去。 void Chat_thread::receive_message()
{QString message socket-readAll(); // 从服务器读取数据emit transfer_recv_info(message); // 发出信号通知接收到的数据
} 处理连接状态变化 state_changed() 函数是一个槽函数用于处理客户端与服务器的连接状态变化。根据不同的 QAbstractSocket::SocketState 枚举值打印调试信息并处理相应状态的变化。 void Chat_thread::state_changed(QAbstractSocket::SocketState socketstate)
{QString stateStr; // 用于保存状态的字符串switch (socketstate){case QAbstractSocket::UnconnectedState:qDebug() \n【状态】与服务器断开连接...;stateStr UnconnectedState;break;
case QAbstractSocket::ConnectedState:stateStr ConnectedState;qDebug() 【状态】与服务器建立连接...;break;
case QAbstractSocket::HostLookupState:stateStr HostLookupState;qDebug() 【状态】正在查找主机...;break;
case QAbstractSocket::ConnectingState:stateStr ConnectingState;qDebug() 【状态】正在连接服务器...;break;
case QAbstractSocket::ClosingState:stateStr ClosingState;qDebug() 【状态】正在关闭连接...;break;
default:stateStr UnknownState;qDebug() 未知的错误, 当前状态: stateStr;break;}
} 断开连接处理 break_connected() 用于处理断开与服务器的连接。当套接字连接断开时关闭并释放资源并发出 connect_cancel 信号通知主线程。 void Chat_thread::break_connected()
{socket-close(); // 关闭套接字socket-deleteLater(); // 延迟删除套接字释放资源emit connect_cancel(); // 发出连接断开信号
} 开始连接服务器 start_connected() 用于发起连接服务器的请求。创建 QTcpSocket 对象并尝试连接到指定的 IP 和端口。连接成功、失败、断开、接收数据等事件都会通过信号槽机制进行处理。 void Chat_thread::start_connected(QString IP, unsigned short PORT)
{socket new QTcpSocket; // 创建套接字对象
socket-connectToHost(QHostAddress(IP), PORT); // 连接到服务器
// 连接成功时发送 connected 信号通知主线程上传消息connect(socket, QTcpSocket::connected, this, Chat_thread::connected);
// 连接失败时处理connect(socket, QTcpSocket::errorOccurred, this, [](QAbstractSocket::SocketError socketError){qDebug() 连接失败;QMessageBox::critical(nullptr, 连接失败, 连接失败错误代码 QString::number(socketError));});
// 连接断开时处理connect(socket, QTcpSocket::disconnected, this, Chat_thread::break_connected);
// 监听数据接收connect(socket, QTcpSocket::readyRead, this, Chat_thread::receive_message);
} 发送消息 start_send() 用于发送消息到服务器。首先检查套接字是否处于连接状态如果已连接则发送消息。如果未连接则发出 not_connected 信号。 void Chat_thread::start_send(QString message)
{if (socket socket-state() QAbstractSocket::ConnectedState){socket-write(message.toUtf8()); // 发送消息}else{emit not_connected(); // 如果未连接发出未连接信号}
}
二、实现效果
1、服务器 2、客户端