diff --git a/internal/platform/implementation/windows/wifi_hotspot.h b/internal/platform/implementation/windows/wifi_hotspot.h index ba019179..5c34e414 100644 --- a/internal/platform/implementation/windows/wifi_hotspot.h +++ b/internal/platform/implementation/windows/wifi_hotspot.h @@ -16,8 +16,8 @@ #define PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_ // Windows headers -#include #include +#include #include // Standard C/C++ headers @@ -27,6 +27,7 @@ #include // Nearby connections headers +#include "absl/strings/string_view.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/implementation/windows/scheduled_executor.h" #include "internal/platform/implementation/windows/submittable_executor.h" @@ -123,7 +124,7 @@ class WifiHotspotSocket : public api::WifiHotspotSocket { Exception Close() override; private: - enum class SocketType {kWinRTSocket = 0, kWin32Socket}; + enum class SocketType { kWinRTSocket = 0, kWin32Socket }; // A simple wrapper to handle input stream of socket class SocketInputStream : public InputStream { public: @@ -204,6 +205,10 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { bool listen(); private: + static constexpr int kSocketEventsCount = 2; + static constexpr int kSocketEventListen = 0; + static constexpr int kSocketEventClose = 1; + // The listener is accepting incoming connections fire_and_forget Listener_ConnectionReceived( StreamSocketListener listener, @@ -214,6 +219,8 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { // Retrieves hotspot IP address from local machine std::string GetHotspotIpAddress() const; + void SocketErrorNotice(absl::string_view reason); + mutable absl::Mutex mutex_; absl::CondVar cond_; SubmittableExecutor submittable_executor_; @@ -225,6 +232,13 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { std::deque pending_client_sockets_ ABSL_GUARDED_BY(mutex_); SOCKET listen_socket_ = INVALID_SOCKET; SOCKET client_socket_ = INVALID_SOCKET; + + // closesocket cannot trigger FD_CLOSE on listener socket. In order to avoid + // blocking in WSAWaitForMultipleEvents, we use a socket event to trigger + // WSAWaitForMultipleEvents safely. + // The socket_events_ has 2 events, the first one is to handle normal socket + // event, and the second one is to handle event to close the socket manually. + WSAEVENT socket_events_[kSocketEventsCount]; // Close notifier absl::AnyInvocable close_notifier_ = nullptr; diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc index 35429cbb..a9d2f6f2 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc @@ -36,7 +36,11 @@ namespace { using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; } // namespace -WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {} +WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) { + for (auto &it : socket_events_) { + it = WSA_INVALID_EVENT; + } +} WifiHotspotServerSocket::~WifiHotspotServerSocket() { Close(); } @@ -55,7 +59,7 @@ std::string WifiHotspotServerSocket::GetIPAddress() const { std::string hotspot_ip_address = GetHotspotIpAddress(); NEARBY_LOGS(INFO) << __func__ - << ": Return hotspot IP address: " << hotspot_ip_address; + << ": Return hotspot IP address: " << hotspot_ip_address; return hotspot_ip_address; } @@ -126,18 +130,26 @@ Exception WifiHotspotServerSocket::Close() { kEnableHotspotWin32Socket)) { if (listen_socket_ != INVALID_SOCKET) { NEARBY_LOGS(INFO) << ": Close listen_socket_: " << listen_socket_; + // Trigger close event manually + WSASetEvent(socket_events_[kSocketEventClose]); + shutdown(listen_socket_, 2); + shutdown(client_socket_, 2); closesocket(listen_socket_); closesocket(client_socket_); - listen_socket_ = INVALID_SOCKET; - client_socket_ = INVALID_SOCKET; for (const auto &pending_socket : pending_client_sockets_) { if (pending_socket != INVALID_SOCKET) closesocket(pending_socket); } + submittable_executor_.Shutdown(); + listen_socket_ = INVALID_SOCKET; + client_socket_ = INVALID_SOCKET; + for (auto &it : socket_events_) { + WSACloseEvent(it); + it = WSA_INVALID_EVENT; + } WSACleanup(); pending_client_sockets_ = {}; } - submittable_executor_.Shutdown(); } else { if (stream_socket_listener_ != nullptr) { stream_socket_listener_.ConnectionReceived(listener_event_token_); @@ -254,14 +266,16 @@ bool WifiHotspotServerSocket::SetupServerSocketWinRT() { return false; } -// Checks for SOCKET_ERROR, this error can come up when trying to bind, listen, -// Getsockname, WSACreateEvent, WSAEventSelect etc. -void SocketErrorNotice(SOCKET socket_to_close, const char *action) { - // const char *actionAttempted = action; - NEARBY_LOGS(WARNING) << "socket error. " << action +void WifiHotspotServerSocket::SocketErrorNotice(absl::string_view reason) { + NEARBY_LOGS(WARNING) << "socket error. " << reason << " failed with error: " << WSAGetLastError(); - - closesocket(socket_to_close); + for (auto &it : socket_events_) { + if (it != WSA_INVALID_EVENT) { + WSACloseEvent(it); + it = WSA_INVALID_EVENT; + } + } + closesocket(listen_socket_); WSACleanup(); } @@ -293,7 +307,7 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { sizeof(flag)); if (bind(listen_socket_, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Bind"); + SocketErrorNotice("Bind"); return false; } NEARBY_LOGS(INFO) << "Bind socket successful"; @@ -302,50 +316,63 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { memset(&serv_addr, 0, size); if (getsockname(listen_socket_, (struct sockaddr *)&serv_addr, &size) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Getsockname"); + SocketErrorNotice("Getsockname"); return false; } port_ = ntohs(serv_addr.sin_port); NEARBY_LOGS(INFO) << "Hotspot Server bound to port: " << port_; - socket_event = WSACreateEvent(); - if (socket_event == nullptr) { - SocketErrorNotice(listen_socket_, "WSACreateEvent"); + socket_events_[kSocketEventListen] = WSACreateEvent(); + if (socket_events_[kSocketEventListen] == WSA_INVALID_EVENT) { + SocketErrorNotice("WSACreateEvent"); return false; } + + socket_events_[kSocketEventClose] = WSACreateEvent(); + if (socket_events_[kSocketEventClose] == WSA_INVALID_EVENT) { + SocketErrorNotice("WSACreateEvent"); + return false; + } + // Associate event types FD_ACCEPT and FD_CLOSE with the listen_socket_ and // socket_event - if (WSAEventSelect(listen_socket_, socket_event, FD_ACCEPT | FD_CLOSE) == - SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "WSAEventSelect"); + if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen], + FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) { + SocketErrorNotice("WSAEventSelect"); return false; } if (::listen(listen_socket_, SOMAXCONN) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Listen"); + SocketErrorNotice("Listen"); return false; } NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_ << " started to listen with socket event: " << socket_event; - submittable_executor_.Execute([this, socket_event]() { + submittable_executor_.Execute([this]() { DWORD index; WSANETWORKEVENTS network_events; // Wait for network events on all sockets - index = - WSAWaitForMultipleEvents(1, &socket_event, FALSE, WSA_INFINITE, FALSE); + index = WSAWaitForMultipleEvents(kSocketEventsCount, socket_events_, FALSE, + WSA_INFINITE, FALSE); NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_ - << " received event: " << socket_event; + << " received event index: " << index; if (index == WSA_WAIT_TIMEOUT || index == WSA_WAIT_FAILED) { NEARBY_LOGS(INFO) << "Hotspot Server Socket timout or failed "; return false; } index = index - WSA_WAIT_EVENT_0; + if (index == kSocketEventClose) { + // the socket is closed by SDK + NEARBY_LOGS(INFO) << "listner socket is closed."; + return false; + } + // Iterate through all events and enumerate - if (WSAEnumNetworkEvents(listen_socket_, socket_event, &network_events) == - SOCKET_ERROR) { + if (WSAEnumNetworkEvents(listen_socket_, socket_events_[index], + &network_events) == SOCKET_ERROR) { NEARBY_LOGS(INFO) << "Iterate through all events failed"; return false; } @@ -361,7 +388,8 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { return false; } - if (WSAEventSelect(listen_socket_, socket_event, 0) == SOCKET_ERROR) { + if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen], + 0) == SOCKET_ERROR) { NEARBY_LOGS(WARNING) << "Remove association between listen_socket_ and event failed: " << WSAGetLastError();