diff --git a/client/client_main.cpp b/client/client_main.cpp index edf58e68aa..2a5121eaec 100644 --- a/client/client_main.cpp +++ b/client/client_main.cpp @@ -474,8 +474,7 @@ int client_main(int argc, char *argv[]) auto positional = parser.positionalArguments(); if (positional.size() == 1) { url = QUrl(positional.constFirst()); - // Supported schemes: fc21://, fc21+local:// - if (!url.isValid() || (!url.scheme().startsWith("fc21"))) { + if (!url.isValid() || url.scheme() != QStringLiteral("fc21")) { // Try with the default protocol url = QUrl(QStringLiteral("fc21://") + positional.constFirst()); // Still no luck @@ -772,7 +771,8 @@ void set_client_state(enum client_states newstate) qFatal(_("There was an error while auto connecting; aborting.")); exit(EXIT_FAILURE); } else { - try_to_autoconnect(url); + start_autoconnecting_to_server(url); + auto_connect = false; // Don't try this again. } } @@ -1078,6 +1078,11 @@ double real_timer_callback() voteinfo_queue_check_removed(); + { + double autoconnect_time = try_to_autoconnect(url); + time_until_next_call = MIN(time_until_next_call, autoconnect_time); + } + if (C_S_RUNNING != client_state()) { return time_until_next_call; } diff --git a/client/clinet.cpp b/client/clinet.cpp index 1e35edc07d..d0ac677e97 100644 --- a/client/clinet.cpp +++ b/client/clinet.cpp @@ -16,10 +16,8 @@ #endif // Qt -#include #include #include -#include #include // utility @@ -58,10 +56,10 @@ #include "clinet.h" // In autoconnect mode, try to connect to once a second -const int AUTOCONNECT_INTERVAL = 500; +#define AUTOCONNECT_INTERVAL 500 // In autoconnect mode, try to connect 100 times -const int MAX_AUTOCONNECT_ATTEMPTS = 100; +#define MAX_AUTOCONNECT_ATTEMPTS 100 /** Close socket and cleanup. This one doesn't print a message, so should @@ -99,93 +97,63 @@ static void client_conn_close_callback(struct connection *pconn) qUtf8Printable(reason)); } -namespace { -/// The URL we are presently connecting to. -QUrl connect_to; - /** - * Called when there is an error on the socket. + Try to connect to a server: + - try to create a TCP socket to the given URL (default to + localhost:DEFAULT_SOCK_PORT). + - if successful: + - start monitoring the socket for packets from the server + - send a "login request" packet to the server + and - return 0 + - if unable to create the connection, close the socket, put an error + message in ERRBUF and return the Unix error code (ie., errno, which + will be non-zero). */ -static void error_on_socket() +static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize) { - if (client.conn.sock != nullptr) { - log_debug("%s", qUtf8Printable(client.conn.sock->errorString())); - real_output_window_append(client.conn.sock->errorString(), NULL); + // Apply defaults + auto url_copy = url; + if (url_copy.host().isEmpty()) { + url_copy.setHost(QStringLiteral("localhost")); + } + if (url_copy.port() <= 0) { + url_copy.setPort(DEFAULT_SOCK_PORT); } - client.conn.used = false; -} -/** - * Try to connect to a server: - * - try to create a TCP socket to the given URL - * - if successful: - * - start monitoring the socket for packets from the server - * - send a "login request" packet to the server - * and - return 0 - * - if unable to create the connection, close the socket, put an error - * message in ERRBUF and return the Unix error code (ie., errno, which - * will be non-zero). - */ -static int try_to_connect(const QUrl &url, char *errbuf, int errbufsize) -{ connections_set_close_callback(client_conn_close_callback); // connection in progress? wait. if (client.conn.used) { - if (url != connect_to) { - (void) fc_strlcpy( - errbuf, _("Canceled previous connection, trying new connection."), - errbufsize); - if (auto tcp = qobject_cast(client.conn.sock)) { - tcp->abort(); - } else if (auto local = - qobject_cast(client.conn.sock)) { - local->abort(); - } - connect_to = url; - } else { - (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize); - return -1; - } - } - - // Reset - if (client.conn.sock) { - client.conn.sock->disconnect(client.conn.sock); - client.conn.sock->deleteLater(); + (void) fc_strlcpy(errbuf, _("Connection in progress."), errbufsize); + return -1; } client.conn.used = true; // Now there will be a connection :) // Connect - if (url.scheme() == QStringLiteral("fc21")) { - QTcpSocket *sock = new QTcpSocket; - client.conn.sock = sock; + if (!client.conn.sock) { + client.conn.sock = new QTcpSocket; QObject::connect( - sock, + client.conn.sock, QOverload::of(&QAbstractSocket::error), - &error_on_socket); - QObject::connect(sock, &QTcpSocket::connected, - [userName = url.userName()] { - make_connection(client.conn.sock, userName); - }); - sock->connectToHost(url.host(), url.port()); - } else if (url.scheme() == QStringLiteral("fc21+local")) { - QLocalSocket *sock = new QLocalSocket; - client.conn.sock = sock; - QObject::connect( - sock, - QOverload::of(&QLocalSocket::error), - &error_on_socket); - QObject::connect(sock, &QLocalSocket::connected, - [userName = url.userName()] { - make_connection(client.conn.sock, userName); - }); - sock->connectToServer(url.path()); + [] { + if (client.conn.sock != nullptr) { + log_debug("%s", qUtf8Printable(client.conn.sock->errorString())); + real_output_window_append(client.conn.sock->errorString(), NULL, + -1); + } + client.conn.used = false; + }); + } + + client.conn.sock->connectToHost(url.host(), url.port()); + if (!client.conn.sock->waitForConnected(-1)) { + errbuf[0] = '\0'; + return -1; } + make_connection(client.conn.sock, url.userName()); return 0; } -} // namespace /** Connect to a freeciv21-server instance -- or at least try to. On success, @@ -212,7 +180,7 @@ int connect_to_server(const QUrl &url, char *errbuf, int errbufsize) /** Called after a connection is completed (e.g., in try_to_connect). */ -void make_connection(QIODevice *sock, const QString &username) +void make_connection(QTcpSocket *sock, const QString &username) { struct packet_server_join_req req; @@ -280,7 +248,7 @@ void disconnect_from_server() */ static int read_from_connection(struct connection *pc, bool block) { - auto socket = pc->sock; + QTcpSocket *socket = pc->sock; bool have_data_for_server = (pc->used && pc->send_buffer && 0 < pc->send_buffer->ndata); @@ -296,7 +264,7 @@ static int read_from_connection(struct connection *pc, bool block) if (block) { // Wait (and block the main event loop) until we get some data - socket->waitForReadyRead(-1); + socket->waitForReadyRead(); } // Consume everything @@ -321,7 +289,7 @@ static int read_from_connection(struct connection *pc, bool block) This function is called when the client received a new input from the server. */ -void input_from_server(QIODevice *sock) +void input_from_server(QTcpSocket *sock) { int nb; @@ -358,15 +326,22 @@ void input_from_server(QIODevice *sock) } } +static bool autoconnecting = false; /** Make an attempt to autoconnect to the server. It returns number of seconds it should be called again. */ -void try_to_autoconnect(const QUrl &url) +double try_to_autoconnect(const QUrl &url) { char errbuf[512]; static int count = 0; + // Don't repeat autoconnect if not autoconnecting or the user + // established a connection by himself. + if (!autoconnecting || client.conn.established) { + return FC_INFINITY; + } + count++; if (count >= MAX_AUTOCONNECT_ATTEMPTS) { @@ -375,32 +350,32 @@ void try_to_autoconnect(const QUrl &url) exit(EXIT_FAILURE); } - if (!client.conn.sock) { - client.conn.sock = new QTcpSocket; + if (try_to_connect(url, errbuf, sizeof(errbuf)) == 0) { + // Success! Don't call me again + autoconnecting = false; + return FC_INFINITY; + } else { + // All errors are fatal + qCritical(_("Error contacting server \"%s\":\n %s\n"), + qUtf8Printable(url.toDisplayString()), errbuf); + exit(EXIT_FAILURE); } +} - try_to_connect(url, errbuf, sizeof(errbuf)); - - auto error_handler = [url] { - if (client.conn.sock != nullptr) { - output_window_printf(ftc_client, - _("Failed to autoconnect to %s: %d of %d " - "attempts, retrying..."), - qUtf8Printable(url.toDisplayString()), count, - MAX_AUTOCONNECT_ATTEMPTS); - QTimer::singleShot(AUTOCONNECT_INTERVAL, - [url]() { try_to_autoconnect(url); }); - } - }; - if (auto tcp = qobject_cast(client.conn.sock)) { - QObject::connect( - tcp, - QOverload::of(&QAbstractSocket::error), - error_handler); - } else if (auto local = qobject_cast(client.conn.sock)) { - QObject::connect( - local, - QOverload::of(&QLocalSocket::error), - error_handler); - } +/** + Start trying to autoconnect to freeciv21-server. Calls + get_server_address(), then arranges for try_to_autoconnect(), which + calls try_to_connect(), to be called roughly every + AUTOCONNECT_INTERVAL milliseconds, until success, fatal error or + user intervention. + */ +void start_autoconnecting_to_server(const QUrl &url) +{ + output_window_printf( + ftc_client, + _("Auto-connecting to \"%s\" every %f second(s) for %d times"), + qUtf8Printable(url.toDisplayString()), 0.001 * AUTOCONNECT_INTERVAL, + MAX_AUTOCONNECT_ATTEMPTS); + + autoconnecting = true; } diff --git a/client/clinet.h b/client/clinet.h index d2e85e51fe..67cb79b449 100644 --- a/client/clinet.h +++ b/client/clinet.h @@ -11,15 +11,16 @@ #pragma once // Forward declarations -class QIODevice; +class QTcpSocket; class QString; class QUrl; int connect_to_server(const QUrl &url, char *errbuf, int errbufsize); -void make_connection(QIODevice *sock, const QString &username); +void make_connection(QTcpSocket *sock, const QString &username); -void input_from_server(QIODevice *sock); +void input_from_server(QTcpSocket *sock); void disconnect_from_server(); -void try_to_autoconnect(const QUrl &url); +double try_to_autoconnect(const QUrl &url); +void start_autoconnecting_to_server(const QUrl &url); diff --git a/client/connectdlg_common.cpp b/client/connectdlg_common.cpp index b233bcd847..280a416ee4 100644 --- a/client/connectdlg_common.cpp +++ b/client/connectdlg_common.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -239,9 +238,6 @@ bool client_start_server(const QString &user_name) return false; } - // Generate a (random) unique name for the local socket - auto uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); - ruleset = QString::fromUtf8(tileset_what_ruleset(tileset)); // Set up the command-line parameters. @@ -249,7 +245,8 @@ bool client_start_server(const QString &user_name) savesdir = QStringLiteral("%1/saves").arg(storage); scensdir = QStringLiteral("%1/scenarios").arg(storage); - arguments << QStringLiteral("--local") << uuid << QStringLiteral("-q") + arguments << QStringLiteral("-p") << port_buf << QStringLiteral("--bind") + << QStringLiteral("localhost") << QStringLiteral("-q") << QStringLiteral("1") << QStringLiteral("-e") << QStringLiteral("--saves") << savesdir << QStringLiteral("--scenarios") << scensdir @@ -300,9 +297,10 @@ bool client_start_server(const QString &user_name) // Local server URL auto url = QUrl(); - url.setScheme(QStringLiteral("fc21+local")); + url.setScheme(QStringLiteral("fc21")); url.setUserName(user_name); - url.setPath(uuid); + url.setHost(QStringLiteral("localhost")); + url.setPort(internal_server_port); // a reasonable number of tries while (connect_to_server( diff --git a/client/gui-qt/fc_client.cpp b/client/gui-qt/fc_client.cpp index 3907853b55..6765fe86de 100644 --- a/client/gui-qt/fc_client.cpp +++ b/client/gui-qt/fc_client.cpp @@ -276,7 +276,7 @@ enum client_pages fc_client::current_page() { return page; } /** Add notifier for server input */ -void fc_client::add_server_source(QIODevice *sock) +void fc_client::add_server_source(QTcpSocket *sock) { connect(sock, &QIODevice::readyRead, this, &fc_client::server_input); @@ -299,7 +299,7 @@ void fc_client::closeEvent(QCloseEvent *event) */ void fc_client::server_input() { - if (auto *socket = dynamic_cast(sender())) { + if (auto *socket = dynamic_cast(sender())) { input_from_server(socket); } } diff --git a/client/gui-qt/fc_client.h b/client/gui-qt/fc_client.h index 201ae0b2f9..5745d788ae 100644 --- a/client/gui-qt/fc_client.h +++ b/client/gui-qt/fc_client.h @@ -112,7 +112,7 @@ class fc_client : public QMainWindow { ~fc_client() override; QWidget *pages[static_cast(PAGE_GAME) + 2]; void fc_main(QApplication *); - void add_server_source(QIODevice *socket); + void add_server_source(QTcpSocket *socket); enum client_pages current_page(); diff --git a/client/gui-qt/gui_main.cpp b/client/gui-qt/gui_main.cpp index 6b18524790..38c644b070 100644 --- a/client/gui-qt/gui_main.cpp +++ b/client/gui-qt/gui_main.cpp @@ -172,7 +172,7 @@ void qtg_sound_bell() This function is called after the client succesfully has connected to the server. */ -void qtg_add_net_input(QIODevice *sock) { king()->add_server_source(sock); } +void qtg_add_net_input(QTcpSocket *sock) { king()->add_server_source(sock); } /** Stop waiting for any server network data. See add_net_input(). diff --git a/client/gui-qt/page_network.cpp b/client/gui-qt/page_network.cpp index 93a58ddefa..f592e0fc40 100644 --- a/client/gui-qt/page_network.cpp +++ b/client/gui-qt/page_network.cpp @@ -438,12 +438,12 @@ void page_network::slot_connect() switch (connection_status) { case LOGIN_TYPE: - client_url().setScheme(QStringLiteral("fc21")); client_url().setUserName(ui.connect_login_edit->text()); client_url().setHost(ui.connect_host_edit->text()); client_url().setPort(ui.connect_port_edit->text().toInt()); - if (connect_to_server(client_url(), errbuf, sizeof(errbuf)) < 0) { + if (connect_to_server(client_url(), errbuf, sizeof(errbuf)) != -1) { + } else { king->set_status_bar(QString::fromUtf8(errbuf)); output_window_append(ftc_client, errbuf); } diff --git a/client/gui-qt/qtg_cxxside.h b/client/gui-qt/qtg_cxxside.h index 5d17cc15a3..e37c9916cb 100644 --- a/client/gui-qt/qtg_cxxside.h +++ b/client/gui-qt/qtg_cxxside.h @@ -52,7 +52,7 @@ void qtg_canvas_put_text(QPixmap *pcanvas, int canvas_x, int canvas_y, void qtg_set_rulesets(int num_rulesets, QStringList rulesets); void qtg_options_extra_init(); -void qtg_add_net_input(QIODevice *sock); +void qtg_add_net_input(QTcpSocket *sock); void qtg_remove_net_input(); void qtg_real_conn_list_dialog_update(void *unused); void qtg_close_connection_dialog(); diff --git a/client/gui_interface.cpp b/client/gui_interface.cpp index c44f03940a..fac8d4d679 100644 --- a/client/gui_interface.cpp +++ b/client/gui_interface.cpp @@ -153,7 +153,7 @@ void options_extra_init(void) { funcs.options_extra_init(); } /** Call add_net_input callback */ -void add_net_input(QIODevice *sock) { funcs.add_net_input(sock); } +void add_net_input(QTcpSocket *sock) { funcs.add_net_input(sock); } /** Call remove_net_input callback diff --git a/client/gui_interface.h b/client/gui_interface.h index 3b9d7711b7..d5632d7a4b 100644 --- a/client/gui_interface.h +++ b/client/gui_interface.h @@ -60,7 +60,7 @@ struct gui_funcs { void (*set_rulesets)(int num_rulesets, QStringList rulesets); void (*options_extra_init)(); - void (*add_net_input)(QIODevice *sock); + void (*add_net_input)(QTcpSocket *sock); void (*remove_net_input)(); void (*real_conn_list_dialog_update)(void *unused); void (*close_connection_dialog)(); diff --git a/client/include/gui_main_g.h b/client/include/gui_main_g.h index 121bba748f..c2fa071432 100644 --- a/client/include/gui_main_g.h +++ b/client/include/gui_main_g.h @@ -11,7 +11,7 @@ #pragma once // Forward declarations -class QIODevice; +class QTcpSocket; // utility #include "support.h" // bool type @@ -28,7 +28,7 @@ GUI_FUNC_PROTO(void, options_extra_init, void) GUI_FUNC_PROTO(void, real_conn_list_dialog_update, void *) GUI_FUNC_PROTO(void, sound_bell, void) -GUI_FUNC_PROTO(void, add_net_input, QIODevice *) +GUI_FUNC_PROTO(void, add_net_input, QTcpSocket *) GUI_FUNC_PROTO(void, remove_net_input, void) GUI_FUNC_PROTO(void, set_unit_icon, int idx, struct unit *punit) diff --git a/common/networking/connection.cpp b/common/networking/connection.cpp index 0190612093..3ad36ffdf2 100644 --- a/common/networking/connection.cpp +++ b/common/networking/connection.cpp @@ -16,7 +16,6 @@ #endif // Qt -#include #include // utility @@ -55,7 +54,7 @@ static void default_conn_close_callback(struct connection *pconn) { fc_assert_msg(conn_close_callback != default_conn_close_callback, "Closing a socket (%s) before calling " - "connections_set_close_callback().", + "close_socket_set_callback().", conn_description(pconn)); } @@ -70,13 +69,13 @@ void connections_set_close_callback(conn_close_fn_t func) /** Call the conn_close_callback. */ -void connection_close(struct connection *pconn, const QString &reason) +void connection_close(struct connection *pconn, const char *reason) { fc_assert_ret(NULL != pconn); if (NULL != reason && pconn->closing_reason.isEmpty()) { // NB: we don't overwrite the original reason. - pconn->closing_reason = reason; + pconn->closing_reason = QString::fromUtf8(reason); } (*conn_close_callback)(pconn); @@ -111,7 +110,7 @@ static bool buffer_ensure_free_extra_space(struct socket_packet_buffer *buf, >0 : number of bytes read =0 : non-blocking sockets only; no data read, would block */ -int read_socket_data(QIODevice *sock, struct socket_packet_buffer *buffer) +int read_socket_data(QTcpSocket *sock, struct socket_packet_buffer *buffer) { int didget; @@ -188,11 +187,7 @@ void flush_connection_send_buffer_all(struct connection *pc) } } if (pc && pc->sock) { - if (auto socket = qobject_cast(pc->sock)) { - socket->flush(); - } else if (auto socket = qobject_cast(pc->sock)) { - socket->flush(); - } + pc->sock->flush(); } } @@ -209,11 +204,7 @@ static void flush_connection_send_buffer_packets(struct connection *pc) } } if (pc && pc->sock) { - if (auto socket = qobject_cast(pc->sock)) { - socket->flush(); - } else if (auto socket = qobject_cast(pc->sock)) { - socket->flush(); - } + pc->sock->flush(); } } diff --git a/common/networking/connection.h b/common/networking/connection.h index 657becf9e6..cbc95e857b 100644 --- a/common/networking/connection.h +++ b/common/networking/connection.h @@ -30,8 +30,7 @@ #include "fc_types.h" // Forward declarations -class QIODevice; -class QString; +class QTcpSocket; struct conn_pattern_list; struct genhash; @@ -127,7 +126,7 @@ struct packet_header { ***********************************************************/ struct connection { int id; /* used for server/client communication */ - QIODevice *sock = nullptr; + QTcpSocket *sock = nullptr; bool used; bool established; // have negotiated initial packets struct packet_header packet_header; @@ -260,9 +259,9 @@ struct connection { typedef void (*conn_close_fn_t)(struct connection *pconn); void connections_set_close_callback(conn_close_fn_t func); -void connection_close(struct connection *pconn, const QString &reason); +void connection_close(struct connection *pconn, const char *reason); -int read_socket_data(QIODevice *sock, struct socket_packet_buffer *buffer); +int read_socket_data(QTcpSocket *sock, struct socket_packet_buffer *buffer); void flush_connection_send_buffer_all(struct connection *pc); bool connection_send_data(struct connection *pconn, const unsigned char *data, int len); diff --git a/server/civserver.cpp b/server/civserver.cpp index 76d7436cb0..80802ac6d5 100644 --- a/server/civserver.cpp +++ b/server/civserver.cpp @@ -176,9 +176,6 @@ int main(int argc, char *argv[]) _("Listen for clients on ADDR"), // TRANS: Command-line argument _("ADDR")}, - {QStringLiteral("local"), _("Listens to the local socket NAME"), - // TRANS: Command-line argument - _("NAME")}, {{"d", _("debug")}, // TRANS: Do not translate "fatal", "critical", "warning", "info" or // "debug". It's exactly what the user must type. @@ -255,7 +252,7 @@ int main(int argc, char *argv[]) #endif // AI_MODULES }); if (!ok) { - qCritical("Adding command line arguments failed"); + qFatal("Adding command line arguments failed"); exit(EXIT_FAILURE); } @@ -292,29 +289,18 @@ int main(int argc, char *argv[]) if (parser.isSet(QStringLiteral("identity"))) { srvarg.ranklog_filename = parser.value(QStringLiteral("identity")); } - if (parser.isSet(QStringLiteral("local"))) { - srvarg.local_addr = parser.value(QStringLiteral("local")); - } if (parser.isSet(QStringLiteral("port"))) { - if (!srvarg.local_addr.isEmpty()) { - qCritical(_("Cannot use --port with --local")); - exit(EXIT_FAILURE); - } bool conversion_ok; srvarg.port = parser.value(QStringLiteral("port")).toUInt(&conversion_ok); srvarg.user_specified_port = true; if (!conversion_ok) { - qCritical(_("Invalid port number %s"), - qUtf8Printable(parser.value("port"))); + qFatal(_("Invalid port number %s"), + qUtf8Printable(parser.value("port"))); exit(EXIT_FAILURE); } } if (parser.isSet(QStringLiteral("bind"))) { - if (!srvarg.local_addr.isEmpty()) { - qCritical(_("Cannot use --bind with --local")); - exit(EXIT_FAILURE); - } srvarg.bind_addr = parser.value(QStringLiteral("bind")); } if (parser.isSet(QStringLiteral("Bind-meta"))) { @@ -328,8 +314,8 @@ int main(int argc, char *argv[]) srvarg.quitidle = parser.value(QStringLiteral("quitidle")).toUInt(&conversion_ok); if (!conversion_ok) { - qCritical(_("Invalid number %s"), - qUtf8Printable(parser.value("quitidle"))); + qFatal(_("Invalid number %s"), + qUtf8Printable(parser.value("quitidle"))); exit(EXIT_FAILURE); } } diff --git a/server/connecthand.cpp b/server/connecthand.cpp index 1a128a7798..e973be6dcb 100644 --- a/server/connecthand.cpp +++ b/server/connecthand.cpp @@ -957,7 +957,7 @@ bool connection_delegate_restore(struct connection *pconn) Close a connection. Use this in the server to take care of delegation stuff (reset the username of the controlled connection). */ -void connection_close_server(struct connection *pconn, const QString &reason) +void connection_close_server(struct connection *pconn, const char *reason) { // Restore possible delegations before the connection is closed. connection_delegate_restore(pconn); diff --git a/server/connecthand.h b/server/connecthand.h index ff2e02cd72..4c348fdc46 100644 --- a/server/connecthand.h +++ b/server/connecthand.h @@ -15,8 +15,6 @@ #include "fc_types.h" -class QString; - struct connection; struct conn_list; struct packet_authentication_reply; @@ -46,5 +44,4 @@ bool connection_delegate_take(struct connection *pconn, struct player *pplayer); bool connection_delegate_restore(struct connection *pconn); -void connection_close_server(struct connection *pconn, - const QString &reason); +void connection_close_server(struct connection *pconn, const char *reason); diff --git a/server/sernet.cpp b/server/sernet.cpp index c79324e5b8..f8c17267be 100644 --- a/server/sernet.cpp +++ b/server/sernet.cpp @@ -24,7 +24,6 @@ // Qt #include #include -#include #include #include #include @@ -127,7 +126,7 @@ void close_connections_and_socket() conn_list_destroy(game.all_connections); conn_list_destroy(game.est_connections); - if (srvarg.announce != ANNOUNCE_NONE && udp_socket) { + if (srvarg.announce != ANNOUNCE_NONE) { udp_socket->close(); delete udp_socket; udp_socket = nullptr; @@ -314,8 +313,7 @@ static const char *makeup_connection_name(int *id) Returns 0 on success, -1 on failure (bad accept(), or too many connections). */ -int server_make_connection(QIODevice *new_sock, const QString &client_addr, - const QString &ip_addr) +int server_make_connection(QTcpSocket *new_sock, const QString &client_addr) { civtimer *timer; int i; @@ -347,7 +345,8 @@ int server_make_connection(QIODevice *new_sock, const QString &client_addr, sz_strlcpy(pconn->username, makeup_connection_name(&pconn->id)); pconn->addr = client_addr; - sz_strlcpy(pconn->server.ipaddr, qUtf8Printable(ip_addr)); + sz_strlcpy(pconn->server.ipaddr, + qUtf8Printable(new_sock->peerAddress().toString())); conn_list_append(game.all_connections, pconn); @@ -373,27 +372,9 @@ int server_make_connection(QIODevice *new_sock, const QString &client_addr, Open server socket to be used to accept client connections and open a server socket for server LAN announcements. */ -std::optional server_open_socket() +QTcpServer *server_open_socket() { - // Local socket mode - if (!srvarg.local_addr.isEmpty()) { - auto server = std::make_unique(); - if (server->listen(srvarg.local_addr)) { - connections_set_close_callback(server_conn_close_callback); - - // Don't do LAN announcements - return server; - } else { - qCritical().noquote() - << QString(_("Server: cannot listen on local socket %1: %2")) - .arg(srvarg.local_addr) - .arg(server->errorString()); - return std::nullopt; - } - } - - // TCP socket mode - auto server = std::make_unique(); + auto *server = new QTcpServer; int max = srvarg.port + 100; for (; srvarg.port < max; ++srvarg.port) { @@ -401,10 +382,7 @@ std::optional server_open_socket() srvarg.bind_addr.isNull() ? qUtf8Printable(srvarg.bind_addr) : "(any)", srvarg.port); - if (server->listen(srvarg.bind_addr.isNull() - ? QHostAddress::Any - : QHostAddress(srvarg.bind_addr), - srvarg.port)) { + if (server->listen(QHostAddress::Any, srvarg.port)) { break; } @@ -418,7 +396,7 @@ std::optional server_open_socket() _("Server: cannot listen on port %1: %2")) .arg(srvarg.port) .arg(server->errorString()))); - return std::nullopt; + return server; } } @@ -564,7 +542,7 @@ void get_lanserver_announcement() return; } - if (udp_socket && udp_socket->hasPendingDatagrams()) { + if (udp_socket->hasPendingDatagrams()) { QNetworkDatagram qnd = udp_socket->receiveDatagram(); auto data = qnd.data(); dio_input_init(&din, data.constData(), 1); diff --git a/server/sernet.h b/server/sernet.h index 4bf25200a2..3eefc7d60a 100644 --- a/server/sernet.h +++ b/server/sernet.h @@ -10,14 +10,9 @@ **************************************************************************/ #pragma once -#include -#include -#include - // Forward declarations -class QIODevice; -class QLocalServer; class QTcpServer; +class QTcpSocket; class QString; struct connection; @@ -28,17 +23,13 @@ struct connection; #define SERVER_LAN_TTL 1 #define SERVER_LAN_VERSION 2 -using socket_server = - std::variant, std::unique_ptr>; - -std::optional server_open_socket(); +QTcpServer *server_open_socket(); void flush_packets(); void incoming_client_packets(connection *pconn); void close_connections_and_socket(); void really_close_connections(); void init_connections(); -int server_make_connection(QIODevice *new_sock, const QString &client_addr, - const QString &ip_addr); +int server_make_connection(QTcpSocket *new_sock, const QString &client_addr); void connection_ping(struct connection *pconn); void handle_conn_pong(struct connection *pconn); void handle_client_heartbeat(struct connection *pconn); diff --git a/server/server.cpp b/server/server.cpp index 6e26bddcfe..db7a4d0faa 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -126,7 +126,7 @@ void fc_interface_init_server() /** Server initialization. */ -std::optional srv_prepare() +std::pair srv_prepare() { // make sure it's initialized srv_init(); @@ -136,9 +136,10 @@ std::optional srv_prepare() con_log_init(srvarg.log_filename); // logging available after this point - auto server = server_open_socket(); - if (!server) { - return std::nullopt; + auto *tcp_server = server_open_socket(); + if (!tcp_server->isListening()) { + // Don't even try to start a game. + return std::make_pair(tcp_server, false); } #if IS_BETA_VERSION @@ -168,7 +169,7 @@ std::optional srv_prepare() success = fcdb_init(qUtf8Printable(srvarg.fcdb_conf)); if (!success) { - return std::nullopt; + return std::make_pair(tcp_server, false); } } @@ -180,7 +181,7 @@ std::optional srv_prepare() if (testfilename.isEmpty()) { qFatal(_("Ruleset directory \"%s\" not found"), qUtf8Printable(srvarg.ruleset)); - return std::nullopt; + return std::make_pair(tcp_server, false); } sz_strlcpy(game.server.rulesetdir, qUtf8Printable(srvarg.ruleset)); } @@ -204,11 +205,11 @@ std::optional srv_prepare() || !send_server_info_to_metaserver(META_INFO)) { con_write(C_FAIL, _("Not starting without explicitly requested " "metaserver connection.")); - return std::nullopt; + return std::make_pair(tcp_server, false); } } - return server; + return std::make_pair(tcp_server, true); } } // anonymous namespace @@ -256,27 +257,20 @@ server::server() // Now init the old C API fc_interface_init_server(); - auto server = srv_prepare(); - if (!server) { + bool success; + std::tie(m_tcp_server, success) = srv_prepare(); + if (!success) { // Could not listen on the specified port. Rely on the caller checking // our state and not starting the event loop. return; } - m_server = std::move(*server); - if (std::holds_alternative>(m_server)) { - auto &tcp = std::get>(m_server); - connect(tcp.get(), &QTcpServer::newConnection, this, - &server::accept_tcp_connections); - connect(tcp.get(), &QTcpServer::acceptError, - [](QAbstractSocket::SocketError error) { - qCritical("Error accepting connection: %d", error); - }); - } else if (std::holds_alternative>( - m_server)) { - auto &local = std::get>(m_server); - connect(local.get(), &QLocalServer::newConnection, this, - &server::accept_local_connections); - } + m_tcp_server->setParent(this); + connect(m_tcp_server, &QTcpServer::newConnection, this, + &server::accept_connections); + connect(m_tcp_server, &QTcpServer::acceptError, + [](QAbstractSocket::SocketError error) { + qCritical("Error accepting connection: %d", error); + }); m_eot_timer = timer_new(TIMER_CPU, TIMER_ACTIVE); @@ -358,57 +352,15 @@ void server::init_interactive() */ bool server::is_ready() const { return m_ready; } -/** - * Server accepts connections over local socket: - * Low level socket stuff, and basic-initialize the connection struct. - */ -void server::accept_local_connections() -{ - // We know it's safe: this method is only called for local connections - auto &server = std::get>(m_server); - - // There may be several connections available. - while (server->hasPendingConnections()) { - auto *socket = server->nextPendingConnection(); - socket->setParent(this); - - if (server_make_connection(socket, QStringLiteral("local"), - QStringLiteral("local")) - == 0) { - // Success making the connection, connect signals - connect(socket, &QIODevice::readyRead, this, &server::input_on_socket); - connect(socket, - QOverload::of( - &QLocalSocket::error), - this, &server::error_on_socket); - - // Prevents quitidle from firing immediately - m_someone_ever_connected = true; - - // Turn off the quitidle timeout if it's running - if (m_quitidle_timer != nullptr) { - m_quitidle_timer->stop(); - m_quitidle_timer->deleteLater(); - m_quitidle_timer = nullptr; - } - } else { - socket->deleteLater(); - } - } -} - /** Server accepts connections from client: Low level socket stuff, and basic-initialize the connection struct. */ -void server::accept_tcp_connections() +void server::accept_connections() { - // We know it's safe: this method is only called for TCP connections - auto &server = std::get>(m_server); - // There may be several connections available. - while (server->hasPendingConnections()) { - auto *socket = server->nextPendingConnection(); + while (m_tcp_server->hasPendingConnections()) { + auto *socket = m_tcp_server->nextPendingConnection(); socket->setParent(this); // Lookup the host name of the remote end. @@ -438,9 +390,9 @@ void server::accept_tcp_connections() { // Use TolerantConversion so one connections from the same address on // IPv4 and IPv6 are rejected as well. - if (const auto *other = qobject_cast(pconn->sock); - socket->peerAddress().isEqual( - other->peerAddress(), QHostAddress::TolerantConversion)) { + if (socket->peerAddress().isEqual( + pconn->sock->peerAddress(), + QHostAddress::TolerantConversion)) { continue; } if (++count >= game.server.maxconnectionsperhost) { @@ -455,14 +407,11 @@ void server::accept_tcp_connections() conn_list_iterate_end; if (!success) { - socket->deleteLater(); continue; } } - if (server_make_connection(socket, remote, - socket->peerAddress().toString()) - == 0) { + if (server_make_connection(socket, remote) == 0) { // Success making the connection, connect signals connect(socket, &QIODevice::readyRead, this, &server::input_on_socket); connect(socket, @@ -532,7 +481,7 @@ void server::error_on_socket() conn_list_iterate(game.all_connections, pconn) { if (pconn->sock == socket) { - connection_close_server(pconn, socket->errorString()); + connection_close_server(pconn, _("network exception")); break; } } @@ -548,7 +497,7 @@ void server::error_on_socket() void server::input_on_socket() { // Get the socket - auto *socket = dynamic_cast(sender()); + auto *socket = dynamic_cast(sender()); if (socket == nullptr) { return; } diff --git a/server/server.h b/server/server.h index e00e0d3d60..9dac70556d 100644 --- a/server/server.h +++ b/server/server.h @@ -19,12 +19,8 @@ #pragma once -#include "sernet.h" - // Qt -#include #include -#include class civtimer; class QTcpServer; @@ -45,8 +41,7 @@ private slots: void error_on_socket(); void input_on_socket(); void input_on_stdin(); - void accept_local_connections(); - void accept_tcp_connections(); + void accept_connections(); void send_pings(); // Higher-level stuff @@ -68,7 +63,7 @@ private slots: bool m_interactive = false; QObject *m_stdin_notifier = nullptr; // Actual type is OS-dependent - socket_server m_server = socket_server(); + QTcpServer *m_tcp_server = nullptr; civtimer *m_eot_timer = nullptr, *m_between_turns_timer = nullptr; diff --git a/server/srv_main.h b/server/srv_main.h index b5f0714936..efb5846ffa 100644 --- a/server/srv_main.h +++ b/server/srv_main.h @@ -29,8 +29,6 @@ struct server_arguments { bool metaconnection_persistent; QString identity_name; unsigned short int metaserver_port; - // Address in case a local socket is used - QString local_addr; // address this server is to listen on (NULL => INADDR_ANY) QString bind_addr; // this server's listen port