Able to send and receive messages, server panicking and not relaying userids yet
This commit is contained in:
34
client/inc/logic/client_reactor.h
Normal file
34
client/inc/logic/client_reactor.h
Normal 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;
|
||||||
|
};
|
||||||
14
client/inc/logic/message_handler.h
Normal file
14
client/inc/logic/message_handler.h
Normal 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);
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 &);
|
||||||
};
|
};
|
||||||
|
|||||||
61
client/src/logic/client_reactor.cpp
Normal file
61
client/src/logic/client_reactor.cpp
Normal 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; }
|
||||||
5
client/src/logic/message_handler.cpp
Normal file
5
client/src/logic/message_handler.cpp
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include "logic/message_handler.h"
|
||||||
|
|
||||||
|
void MessageHandler::emitMessageReceived(const chat::chatMsg &msg) {
|
||||||
|
emit messageReceived(msg);
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user