diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md index c7a39ac6..515488f7 100644 --- a/doc/en/scripting/builtins/libnetwork.md +++ b/doc/en/scripting/builtins/libnetwork.md @@ -132,3 +132,10 @@ network.get_total_upload() --> int -- in bytes. network.get_total_download() --> int ``` + +## Other + +```lua +-- Looks for a free port to use. +network.find_free_port() --> int +``` diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md index b7b1bdf5..269b1615 100644 --- a/doc/ru/scripting/builtins/libnetwork.md +++ b/doc/ru/scripting/builtins/libnetwork.md @@ -197,3 +197,10 @@ network.get_total_upload() --> int -- в байтах. network.get_total_download() --> int ``` + +## Другое + +```lua +-- Ищет свободный для использования порт. +network.find_free_port() --> int +``` diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 77fc902e..580176cd 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -409,6 +409,10 @@ static int l_get_total_download(lua::State* L, network::Network& network) { return lua::pushinteger(L, network.getTotalDownload()); } +static int l_find_free_port(lua::State* L, network::Network& network) { + return lua::pushinteger(L, network.findFreePort()); +} + static int l_set_nodelay(lua::State* L, network::Network& network) { u64id_t id = lua::tointeger(L, 1); bool noDelay = lua::toboolean(L, 2); @@ -534,6 +538,7 @@ const luaL_Reg networklib[] = { {"__post", wrap}, {"get_total_upload", wrap}, {"get_total_download", wrap}, + {"find_free_port", wrap}, {"__pull_events", wrap}, {"__open_tcp", wrap}, {"__open_udp", wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index ae3a2ea5..868273bf 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -40,6 +40,8 @@ namespace network { int port, const ServerDatagramCallback& handler ); + + int find_free_port(); } @@ -90,6 +92,10 @@ Server* Network::getServer(u64id_t id, bool includePrivate) const { return found->second.get(); } +int Network::findFreePort() const { + return find_free_port(); +} + u64id_t Network::connectTcp( const std::string& address, int port, diff --git a/src/network/Network.hpp b/src/network/Network.hpp index 89e27e59..6d5cd6d5 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -88,6 +88,8 @@ namespace network { [[nodiscard]] Connection* getConnection(u64id_t id, bool includePrivate); [[nodiscard]] Server* getServer(u64id_t id, bool includePrivate) const; + int findFreePort() const; + u64id_t connectTcp( const std::string& address, int port, diff --git a/src/network/Sockets.cpp b/src/network/Sockets.cpp index 7b6f425f..86972ae7 100644 --- a/src/network/Sockets.cpp +++ b/src/network/Sockets.cpp @@ -443,6 +443,7 @@ public: closesocket(descriptor); throw std::runtime_error("could not bind port "+std::to_string(port)); } + port = ntohs(address.sin_port); logger.info() << "opened server at port " << port; auto server = std::make_shared(id, network, descriptor, port); @@ -721,4 +722,24 @@ namespace network { ) { return SocketUdpServer::openServer(id, network, port, handler); } + + int find_free_port() { + SOCKET descriptor = socket(AF_INET, SOCK_STREAM, 0); + if (descriptor == -1) { + return -1; + } + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = 0; + if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) { + closesocket(descriptor); + return -1; + } + socklen_t len = sizeof(address); + getsockname(descriptor, (sockaddr*)&address, &len); + int port = ntohs(address.sin_port); + closesocket(descriptor); + return port; + } }