Connect to internet
-
I have created a small game for Android to be played between two players on different phones, using TCP. I have created a QTcpServer on one phone and have the other as a client. This works well when they are on the same WiFi, connecting with 192.168.1.x.
However, I also want it to be possible to play when they are on different networks/mobile data. How can I accomplish this? I am very new to network programming and just have some very basic knowledge of networks in general. Is it easy to add this functionality to my application using QTcpServer and QTcpSocket? Do you know an easy-to-follow guide for this if it is a little more complicated?
Thanks
-
@cludic
To play over Internet you need some sort of public server, because you don't want to open your device to the whole world.
It's not that easy to create Internet services and it's not possible using onlyQTcpSocket
/QTcpServer
, unless yourserver
is already "public". -
Thanks for such a quick answer.
So when I tell the QTcpServer to listen to QHostAddress::AnyIPv4, it is in fact only listening for devices on the local network?
If this is the case, that means I can at least feel comfortable that my application is safe, right? (Unless someone hacks the network itself)
How complicated is it to make one of the two phones a "public server" in a safe way?
-
Write a server in Node.js and JavaScript. Deploy it on Heroku (it is a free hosing). Use WebSockets. This is my example how to connect Qt C++ using QWebSocket with Node.js: https://github.com/8Observer8/websocket-connection (Qt Client is included to repo) But I use TypeScript for server there. This is my JavaScript example that ready do deploy on Heroku: https://github.com/8Observer8/multiplayer-game-in-js It is simple to deploy on Heroku. Just create a new application on Heroku. Go to Deploy Section of Heroku and connect your app to repo (By clicking on Connect button and click "Deploy"
app.js
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", (wss) => { console.log("connected") }); const port = process.env.PORT || 3000; httpServer.listen(port, () => { console.log("Server started. Port: ", port) });
Web client:
public/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Hello</h1> <script> const ws = new WebSocket("wss://multiplayer-game-in-js.herokuapp.com"); ws.onopen = () => { console.log("connected"); } </script> </body> </html>
-
Thanks a lot for the suggestions, I will look in to them!
I am still not sure how the QTcpServer works exactly. Is it only listening on the local network when using QHostAddress::AnyIPv4?
Is it "safe" to use a QTcpServer that is listening for QHostAddress::AnyIPv4? I mean, if it is possible for computers outside the local network, is there any risks using it?
After reading your posts, it sounds like it will take some time to make it possible to play over internet, so I think I will stick to the local network while I try to learn more. Do you have any other suggestions when using QTcpServer for a local network?
-
@cludic said in Connect to internet:
I am still not sure how the QTcpServer works exactly. Is it only listening on the local network when using QHostAddress::AnyIPv4?
It seems like you don't know much about the IP-protocol, networking and routing...
There are public IPs and your private network IPs.
Public IPs are unique, while every private network uses the same private IP address range- 192.168.0.0 - 192.168.255.255
- 172.16.0.0 - 172.31.255.255
- 10.0.0.0 - 10.255.255.255
Player A and player B could have the same local IP address when playing against each other over the internet, since they are in different local networks.
-> Routing, NAT, etcQTcpServer
won't create a public accessible "server" to connect to.So no device "from outside" can attack your client using your local IPv4 address. There is your local router which hides its private networks from the rest (when beeing in your WLAN) and your mobile ISP also uses firewalls and routing when you connect to the next cell tower (you also get a private address).
BTW: The internet doesn't use IPv4 anymore. The connection between public routers is made with IPv6. If IPv4 packages are needed, they will be transferred inside IPv6 packages...
but this is not a thing, you have to care about ;-)One possible way is, to open some ports on your local router and ask your ISP for static IPs.
-> DONT do it!! Your local network would be an easier target to attack then.Safest way would be to rent some public server space and run your public app server from there. If you want to have a 247, (worldwide) game server.
(I think, this is how most of the mobile games, like "Clash of Clans" work, except the dev studios have their own dedicated public game servers, which every "client" - your device - connects to, when starting the game. )Or just have a look at @8Observer8 and @SGaist suggestions.
Read more about the stuff, you need there first and then think about what you are willing to do.
It can not be done with your Qt code only.
-
Thanks for such an informative answer! It certainly seems that I have a lot to learn, it was quite a bit more complicated than I thought. I will do some reading and also study the other suggestions in this thread before giving it another try.
Thanks again!
-
I made a simple example for you (with Qt Client and Node.js server). My example works like this:
- A client sends coordinates of mouse click to server.
- The server receives the coordinates and send them back to the client.
- The client receives the coordinates and shows them on the window.
I have: a Qt client, a web client, and a node.js server. I got the Qt client example from here: Echo Client Example If you want to run this example locally you should write:
m_webSocket.open(QUrl("ws://localhost:3000"));
But if you what to connect to Heroku Node.js server you should write (change to your application name):
m_webSocket.open(QUrl("wss://mouse-click-js.herokuapp.com"));
Deploying on Heroku is very simple. You should to create Heroku app and GitHub repo. Then just connect your app to your repo and click "Deploy" like on the screenshots:
You can run the web client: https://mouse-click-js.herokuapp.com/
Or the Qt release for Windows to test it: https://dl.dropboxusercontent.com/s/t4jvaehloajj3bd/MouseClickClient_ReleaseForWindows.zip
What you need to deploy Qt client on Windows:
Source code of Qt Client: https://github.com/8Observer8/MouseClick_QtClient_Qt5Cpp
Source code of Node.js server: https://github.com/8Observer8/mouse-click-jsmain.cpp
#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(); }
package.json
{ "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" } }
app.js
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); });
index.html
<!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>
-
Thanks so much for taking the time to create this example, it will be really helpful!