Able to send and receive messages, server panicking and not relaying userids yet

This commit is contained in:
2026-02-09 20:35:17 +01:00
parent a0d9d9c6de
commit 38f6488ba1
12 changed files with 171 additions and 13 deletions

View File

@@ -0,0 +1,34 @@
#pragma once
#include "logic/message_handler.h"
#include "messages.pb.h"
#include "services.grpc.pb.h"
#include <QObject>
#include <condition_variable>
#include <grpcpp/support/client_callback.h>
#include <grpcpp/support/status.h>
#include <memory>
class Reactor : public QObject,
public grpc::ClientBidiReactor<chat::chatMsg, chat::chatMsg> {
Q_OBJECT
public:
explicit Reactor(chat::Chat::Stub *);
void OnWriteDone(bool ok) override;
void OnReadDone(bool ok) override;
void OnDone(const grpc::Status &s) override;
grpc::Status Await();
void SendMessage(const chat::chatMsg &msg);
bool IsConnected() const;
std::shared_ptr<MessageHandler> getHandler();
private:
chat::Chat::Stub *m_stub;
grpc::ClientContext m_context;
chat::chatMsg m_msg;
std::shared_ptr<MessageHandler> m_handler;
std::mutex m_mutex;
std::condition_variable m_cv;
bool m_done = false;
grpc::Status m_status;
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "messages.pb.h"
#include <QObject>
class MessageHandler : public QObject {
Q_OBJECT
public:
MessageHandler(QObject *parent = nullptr) {}
void emitMessageReceived(const chat::chatMsg &message);
signals:
void messageReceived(const chat::chatMsg &message);
};

View File

@@ -5,13 +5,13 @@
class Message { class Message {
public: public:
Message(int, QDateTime, QString); Message(QString, QDateTime, QString);
~Message() {} ~Message() {}
QString toString() const; QString toString() const;
private: private:
int m_userId; QString m_userId;
QDateTime m_timestamp; QDateTime m_timestamp;
QString m_content; QString m_content;
}; };

View File

@@ -1,7 +1,10 @@
#pragma once #pragma once
#include "logic/client_reactor.h"
#include "messages.pb.h"
#include "models/chatroom.h" #include "models/chatroom.h"
#include <QMainWindow> #include <QMainWindow>
#include <memory>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { namespace Ui {
@@ -17,9 +20,12 @@ public:
~MainWindow(); ~MainWindow();
private: private:
void sendMsg(const QString &);
Ui::MainWindow *ui; Ui::MainWindow *ui;
Chatroom m_chatroom; Chatroom m_chatroom;
std::unique_ptr<Reactor> m_reactor;
public slots: public slots:
void receiveMsg(QString &); void receiveMsg(const chat::chatMsg &);
}; };

View File

@@ -0,0 +1,61 @@
#include "logic/client_reactor.h"
#include "messages.pb.h"
#include "services.grpc.pb.h"
#include <QObject>
#include <memory>
Reactor::Reactor(chat::Chat::Stub *stub) : m_stub(stub) {
m_stub->async()->sendMsg(&m_context, this);
m_handler = std::make_shared<MessageHandler>();
StartRead(&m_msg);
StartCall();
}
void Reactor::OnWriteDone(bool ok) {
if (!ok) {
std::cerr << "Error: write failed" << std::endl;
}
}
void Reactor::OnReadDone(bool ok) {
if (ok) {
m_handler->emitMessageReceived(m_msg);
std::cout << "Received message from: " << m_msg.userid()
<< " content: " << m_msg.message() << std::endl;
StartRead(&m_msg);
} else {
std::cout << "Server finished sending" << std::endl;
}
}
void Reactor::OnDone(const grpc::Status &status) {
m_status = status;
{
std::lock_guard<std::mutex> lock(m_mutex);
m_done = true;
}
m_cv.notify_one();
if (!status.ok()) {
std::cerr << "Error: " << status.error_message() << std::endl;
}
std::cout << "Server finished sending" << std::endl;
}
grpc::Status Reactor::Await() {
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [this] { return m_done; });
return m_status;
}
void Reactor::SendMessage(const chat::chatMsg &msg) {
static chat::chatMsg writeMsg;
writeMsg.CopyFrom(msg);
StartWrite(&writeMsg);
std::cout << "Sent message: " << msg.message();
}
bool Reactor::IsConnected() const { return !m_done; }
std::shared_ptr<MessageHandler> Reactor::getHandler() { return m_handler; }

View File

@@ -0,0 +1,5 @@
#include "logic/message_handler.h"
void MessageHandler::emitMessageReceived(const chat::chatMsg &msg) {
emit messageReceived(msg);
}

View File

@@ -1,6 +1,6 @@
#include "models/message.h" #include "models/message.h"
Message::Message(int userId, QDateTime timestamp, QString content) Message::Message(QString userId, QDateTime timestamp, QString content)
: m_userId(userId), m_timestamp(timestamp), m_content(content) {} : m_userId(userId), m_timestamp(timestamp), m_content(content) {}
QString Message::toString() const { QString Message::toString() const {

View File

@@ -1,20 +1,45 @@
#include "ui/mainwindow.h" #include "ui/mainwindow.h"
#include "logic/client_reactor.h"
#include "messages.pb.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <memory>
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) { : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this); ui->setupUi(this);
auto channel = grpc::CreateChannel("localhost:50051",
grpc::InsecureChannelCredentials());
auto stub = chat::Chat::NewStub(channel);
m_reactor = std::make_unique<Reactor>(stub.get());
connect(ui->sendButton, &QPushButton::clicked, this, [this]() { connect(ui->sendButton, &QPushButton::clicked, this, [this]() {
auto msg = ui->inputText->toPlainText(); auto msg = ui->inputText->toPlainText();
receiveMsg(msg); if (!msg.isEmpty()) {
sendMsg(msg);
ui->inputText->clear();
}
}); });
connect(m_reactor->getHandler().get(), &MessageHandler::messageReceived, this,
&MainWindow::receiveMsg);
} }
MainWindow::~MainWindow() { delete ui; } MainWindow::~MainWindow() { delete ui; }
void MainWindow::receiveMsg(QString &msg) { void MainWindow::sendMsg(const QString &msg) {
Message message(0, QDateTime::currentDateTime(), msg); if (m_reactor && m_reactor->IsConnected()) {
chat::chatMsg chatMsg;
chatMsg.set_message(msg.toStdString());
m_reactor->SendMessage(chatMsg);
}
}
void MainWindow::receiveMsg(const chat::chatMsg &chatMsg) {
auto userId = QString::fromStdString(chatMsg.userid());
auto content = QString::fromStdString(chatMsg.message());
Message message(userId, QDateTime::currentDateTime(), content);
m_chatroom.addMessage(message); m_chatroom.addMessage(message);
ui->outputText->setText(m_chatroom.getMessagesString()); ui->outputText->setText(m_chatroom.getMessagesString());
} }

View File

@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.23) cmake_minimum_required(VERSION 3.23)
project(chat_server LANGUAGES CXX) project(chat_server LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
file(GLOB_RECURSE SOURCES "src/*.ui" "src/*.cpp" "inc/*.h" "res/*.qrc") file(GLOB_RECURSE SOURCES "src/*.ui" "src/*.cpp" "inc/*.h" "res/*.qrc")
add_executable(chat_server add_executable(chat_server

View File

@@ -5,6 +5,7 @@
#include "services.grpc.pb.h" #include "services.grpc.pb.h"
#include <grpcpp/server_context.h> #include <grpcpp/server_context.h>
#include <grpcpp/support/server_callback.h> #include <grpcpp/support/server_callback.h>
#include <map>
class Service : public chat::Chat::CallbackService { class Service : public chat::Chat::CallbackService {
public: public:
@@ -14,7 +15,7 @@ public:
void sendToAll(const chat::chatMsg &msg); void sendToAll(const chat::chatMsg &msg);
private: private:
std::vector<ChatReactor *> m_clients; std::map<std::string, ChatReactor *> m_clients;
absl::Mutex m_mu; absl::Mutex m_mu;
std::vector<chat::chatMsg> m_messages ABSL_GUARDED_BY(m_mu); std::vector<chat::chatMsg> m_messages ABSL_GUARDED_BY(m_mu);
}; };

View File

@@ -9,6 +9,7 @@ ChatReactor::ChatReactor(Service *service, absl::Mutex *mu,
void ChatReactor::OnReadDone(bool ok) { void ChatReactor::OnReadDone(bool ok) {
if (ok) { if (ok) {
std::cout << "Received message: " << m_msg.message() << std::endl;
m_mu->lock(); m_mu->lock();
m_messages->push_back(m_msg); m_messages->push_back(m_msg);
m_mu->unlock(); m_mu->unlock();

View File

@@ -1,21 +1,29 @@
#include "logic/service.h" #include "logic/service.h"
#include "logic/reactor.h" #include "logic/reactor.h"
#include <cstdlib>
#include <format>
#include <utility>
Service::~Service() { Service::~Service() {
for (auto *client : m_clients) { for (auto client : m_clients) {
delete client; delete client.second;
} }
} }
grpc::ServerBidiReactor<chat::chatMsg, chat::chatMsg> * grpc::ServerBidiReactor<chat::chatMsg, chat::chatMsg> *
Service::sendMsg(grpc::CallbackServerContext *context) { Service::sendMsg(grpc::CallbackServerContext *context) {
auto newClient = new ChatReactor(this, &m_mu, &m_messages); auto newClient = new ChatReactor(this, &m_mu, &m_messages);
m_clients.push_back(newClient); std::string newId = std::format("user_{}", std::rand());
m_clients.insert(std::make_pair(newId, newClient));
std::cout << "New client: " << newId << std::endl;
return newClient; return newClient;
} }
void Service::sendToAll(const chat::chatMsg &msg) { void Service::sendToAll(const chat::chatMsg &msg) {
for (auto *client : m_clients) { std::cout << "Relay message to: ";
client->StartWrite(&msg); for (auto client : m_clients) {
std::cout << client.first << " ";
client.second->StartWrite(&msg);
} }
std::cout << std::endl;
} }