Содержание блога
Heroku удалил бесплатный хостинг в ноябре 2022. Есть альтернативы, например:- https://glitch.com/pricing - даётся 1000 часов на месяц. Файлы проектов будут открыты всем пользователям. Если к приложению не обращаются 5 минут, оно засыпает и просыпается в течение 30 секунду: "Это может привести к задержке ответа до 30 секунд для первого запроса, поступающего после периода бездействия.". Есть бесплатная база данных SQLite.
- У хостинга Render ограничение на бесплатное использование 750 часов в месяц: https://render.com/docs/free. После 15 минут неактивности засыпает. Бесплатная база данных PostgreSQL удаляется через 90 дней. Есть возможность разместить SQLite.
- Подборка бесплатных хостингов для баз данных

Инструкция по развёртыванию Node.js сервера с WebSockets на бесплатном хостинге Heroku. Веб-клиент и Desktop-клиент (Qt)
Рабочий пример: https://mouse-click-js.herokuapp.com/ в котором координаты клика мыши отправляются на сервер и возвращается ответ клиенту в виде сообщения, которое он выводит на экран.
Инструкция по развёртыванию сервера на Heroku:
- На сайте Heroku в правом верхнем углу выбрать "New" -> "Create new app":

- Набрать имя игры и выбрать регион для сервера и нажать кнопку "Create app":

- На вкладке "Deploy" нажать кнопку "Connect to GitHub":

- Появится поле, где нужно ввести имя репозитория, нажать "Search":

- Появится кнопка "Deploy Branch", которая загружает приложение из GitHub, либо можно нажать кнопку "Enable Automatic Deploy", которая активирует автоматическую загрузку из GitHub всякий раз, когда вы делаете у себя на компьютере "git push"
- Приложение будет развёрнуто и у вас будет ссылка на приложение
Примечание. Бесплатный хостинг на Heroku имеет ограничения. Например, если к серверу не было обращений в течение 30 минут, то он засыпает. При следующем обращении отклик будет получен через 5-10 секунд, которые требуются, чтобы разбудить сервер. База данных MySQL имеет ограничение 5 МБайт. На сайте Heroku можно ознакомиться с другими ограничениями бесплатного аккаунта.
Исходники сервера + веб-клиента на JavaScript: https://github.com/8Observer8/mouse-click-js
app.js
| JavaScript | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| const express = require("express");
const http = require("http");
const ws = require("ws");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "./public")));
app.get("/", (req, res) => { res.sendFile(path.join(__dirname, "index.html")) });
const httpServer = http.createServer(app);
const wss = new ws.Server({ server: httpServer });
wss.on("connection",
(ws) =>
{
console.log("Client connected");
ws.onmessage =
(event) =>
{
const msg = JSON.parse(event.data);
console.log(msg.x + ", " + msg.y);
// Send an answer
const resp = {
x: msg.x,
y: msg.y
}
ws.send(JSON.stringify(resp));
}
});
const port = process.env.PORT || 3000;
httpServer.listen(port, () => { console.log("Server started. Port: ", port); }); |
|
public/index.html
| PHP/HTML | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Client</title>
</head>
<body>
<h3>Click on the window.</h3>
<div id="output" style="font-family: Arial; font-size: 14px;"></div>
<script>
const output = document.getElementById("output");
output.innerHTML = "Wait for connection...";
// const ws = new WebSocket("ws://localhost:3000");
const ws = new WebSocket("wss://mouse-click-js.herokuapp.com");
ws.onopen =
() => {
output.innerHTML = "Connected to the server";
window.onclick = (event) => {
const x = event.clientX;
const y = event.clientY;
console.log(x + ", " + y)
ws.send(JSON.stringify({x: x, y: y}));
};
ws.onmessage = (event) => {
console.log(event.data);
const msg = JSON.parse(event.data);
output.innerHTML = `Answer from server: x = ${msg.x}, y = ${msg.y}`;
}
};
</script>
</body>
</html> |
|
package.json
| JSON | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"name": "mouse-click-js",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"ws": "^7.3.1"
}
} |
|
Исходники клиента на Qt C++: https://github.com/8Observer8/... ent_Qt5Cpp
main.cpp
| C++ (Qt) | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| #include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QtWidgets/QLabel>
#include <QtWidgets/QVBoxLayout>
#include <QtGui/QMouseEvent>
#include <QtWebSockets/QWebSocket>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QDebug>
class Window : public QWidget {
Q_OBJECT
private:
QWebSocket m_webSocket;
QLabel m_connection;
QLabel m_output;
public:
Window(QWidget *parent = nullptr) : QWidget(parent) {
setWindowTitle("Qt C++ Client");
resize(300, 300);
QVBoxLayout *vbox = new QVBoxLayout(this);
QLabel *instruction = new QLabel("Click on the window.");
m_connection.setText("Wait for connection...");
QFont font = QFont("Arial", 14);
instruction->setFont(font);
m_connection.setFont(font);
m_output.setFont(font);
vbox->addWidget(instruction);
vbox->addWidget(&m_connection);
vbox->addWidget(&m_output);
vbox->addStretch(1);
connect(&m_webSocket, &QWebSocket::connected,
this, &Window::onConnected);
// m_webSocket.open(QUrl("ws://localhost:3000"));
m_webSocket.open(QUrl("wss://mouse-click-js.herokuapp.com"));
}
private slots:
void onConnected() {
m_connection.setText("Connected to server");
connect(&m_webSocket, &QWebSocket::textMessageReceived,
this, &Window::onMessageReceived);
}
void onMessageReceived(QString message) {
qDebug() << message;
QJsonDocument doc(QJsonDocument::fromJson(message.toUtf8()));
QJsonObject data = doc.object();
int x = data["x"].toInt();
int y = data["y"].toInt();
// Show data
m_output.setText(QString("Answer from server: %1, %2").arg(x).arg(y));
}
private:
void mousePressEvent(QMouseEvent *event) override {
Q_UNUSED(event);
qDebug() << event->x() << " " << event->y();
QJsonObject jsonObject;
jsonObject["x"] = event->x();
jsonObject["y"] = event->y();
QString message = QJsonDocument(jsonObject).toJson(QJsonDocument::Compact);
m_webSocket.sendTextMessage(message);
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
} |
|
MouseClick_QtClient_Qt5Cpp.pro
| Code | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| QT += core gui websockets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp
HEADERS +=
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target |
|
|