diff --git a/hueplusplus/LinHttpHandler.cpp b/hueplusplus/LinHttpHandler.cpp index 5fae413..8ad676a 100755 --- a/hueplusplus/LinHttpHandler.cpp +++ b/hueplusplus/LinHttpHandler.cpp @@ -1,20 +1,20 @@ /** - \file LinHttpHandler.cpp - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + \file LinHttpHandler.cpp + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **/ #include "include/LinHttpHandler.h" @@ -31,173 +31,185 @@ #include // printf, sprintf #include // exit #include // functions for C style null-terminated strings +#include #include // read, write, close class SocketCloser { - public: SocketCloser(int sockFd) :s(sockFd) {} - ~SocketCloser() { close(s); } - private: int s; +public: + SocketCloser(int sockFd) : s(sockFd) {} + ~SocketCloser() { close(s); } +private: + int s; }; std::string LinHttpHandler::send(const std::string & msg, const std::string & adr, int port) const { - // create socket - int socketFD = socket(AF_INET, SOCK_STREAM, 0); - - SocketCloser closeMySocket(socketFD); - if (socketFD < 0) - { - std::cerr << "linHttpHandler: Failed to open socket: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: Failed to open socket")); - } - - // lookup ip address - hostent *server; - server = gethostbyname(adr.c_str()); - if (server == NULL) - { - std::cerr << "linHttpHandler: Failed to find host with address " << adr << ": " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: Failed to find host")); - } - - // fill in the structure - sockaddr_in server_addr; - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length); - - // connect the socket - if (connect(socketFD, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) - { - std::cerr << "linHttpHandler: Failed to connect socket: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: Failed to connect socket")); - } - - // send the request - size_t total = msg.length(); - ssize_t sent = 0; - do - { - ssize_t bytes = write(socketFD, msg.c_str() + sent, total - sent); - if (bytes < 0) - { - std::cerr << "linHttpHandler: Failed to write message to socket: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: Failed to write message to socket")); - } - if (bytes == 0) - { - break; - } - if (bytes) - { - sent += bytes; - } - } while (sent < total); - - // receive the response - std::string response; - total = sizeof(response) - 1; - int received = 0; - char buffer[128] = {}; - do - { - ssize_t bytes = read(socketFD, buffer, 127); - if (bytes < 0) - { - std::cerr << "linHttpHandler: Failed to read response from socket: " << std::strerror(errno) << std::endl; - throw(std::runtime_error("linHttpHandler: Failed to read response from socket")); - } - if (bytes == 0) - { - break; - } - if (bytes) - { - received += bytes; - response.append(buffer, bytes); - } - } while (true); - - if (received == total) - { - std::cerr << "LinHttpHandler: Failed to store complete response from socket\n"; - throw(std::runtime_error("LinHttpHandler: Failed to store complete response from socket")); - } - - return response; + // create socket + int socketFD = socket(AF_INET, SOCK_STREAM, 0); + + SocketCloser closeMySocket(socketFD); + if (socketFD < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: Failed to open socket: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to open socket")); + } + + // lookup ip address + hostent *server; + server = gethostbyname(adr.c_str()); + if (server == NULL) + { + int errCode = errno; + std::cerr << "LinHttpHandler: Failed to find host with address " << adr << ": " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: gethostbyname")); + } + + // fill in the structure + sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length); + + // connect the socket + if (connect(socketFD, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: Failed to connect socket: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to connect socket")); + } + + // send the request + size_t total = msg.length(); + ssize_t sent = 0; + do + { + ssize_t bytes = write(socketFD, msg.c_str() + sent, total - sent); + if (bytes < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: Failed to write message to socket: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to write message to socket")); + } + if (bytes == 0) + { + break; + } + if (bytes) + { + sent += bytes; + } + } while (sent < total); + + // receive the response + std::string response; + total = sizeof(response) - 1; + int received = 0; + char buffer[128] = {}; + do + { + ssize_t bytes = read(socketFD, buffer, 127); + if (bytes < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: Failed to read response from socket: " << std::strerror(errCode) << std::endl; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: Failed to read response from socket")); + } + if (bytes == 0) + { + break; + } + if (bytes) + { + received += bytes; + response.append(buffer, bytes); + } + } while (true); + + if (received == total) + { + std::cerr << "LinHttpHandler: Failed to store complete response from socket\n"; + throw(std::runtime_error("LinHttpHandler: Failed to store complete response from socket")); + } + + return response; } std::vector LinHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const { - hostent *server; // host information - sockaddr_in server_addr; // server address - - //fill in the server's address and data - memset((char*)&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - - // look up the address of the server given its name - server = gethostbyname(adr.c_str()); - if (!server) - { - std::cerr << "linHttpHandler: sendMulticast: Failed to obtain address of " << msg << ": " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: Failed to obtain address of host")); - } - - // put the host's address into the server address structure - memcpy( (void *)&server_addr.sin_addr, server->h_addr_list[0], server->h_length ); - - // create the socket - int socketFD = socket(AF_INET, SOCK_DGRAM, 0); - SocketCloser closeMySendSocket(socketFD); - if (socketFD < 0) - { - std::cerr << "linHttpHandler: sendMulticast: Failed to open socket: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: Failed to open socket")); - } - - // send a message to the server - if (sendto(socketFD, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) - { - std::cerr << "linHttpHandler: sendMulticast: Failed to send message: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: Failed to send message")); - } - - std::string response; - char buffer[2048] = {}; // receive buffer - ssize_t bytesReceived = 0; - - std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); - while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) - { - bytesReceived = recv(socketFD, &buffer, 2048, MSG_DONTWAIT); - if (bytesReceived < 0) - { - if (errno != EAGAIN && errno != EWOULDBLOCK) - { - std::cerr << "linHttpHandler: sendMulticast: Failed to read response from socket: " << std::strerror(errno) << "\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: Failed to read response from socket")); - } - continue; - } - if (bytesReceived) - { - response.append(buffer, bytesReceived); - } - } - - // construct return vector - std::vector returnString; - size_t pos = response.find("\r\n\r\n"); - size_t prevpos = 0; - while (pos != std::string::npos) - { - returnString.push_back(response.substr(prevpos, pos-prevpos)); - pos += 4; - prevpos = pos; - pos = response.find("\r\n\r\n", pos); - } - return returnString; + hostent *server; // host information + sockaddr_in server_addr; // server address + + //fill in the server's address and data + memset((char*)&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + + // look up the address of the server given its name + server = gethostbyname(adr.c_str()); + if (!server) + { + int errCode = errno; + std::cerr << "LinHttpHandler: sendMulticast: Failed to obtain address of " << msg << ": " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to obtain address of host")); + } + + // put the host's address into the server address structure + memcpy((void *)&server_addr.sin_addr, server->h_addr_list[0], server->h_length); + + // create the socket + int socketFD = socket(AF_INET, SOCK_DGRAM, 0); + SocketCloser closeMySendSocket(socketFD); + if (socketFD < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: sendMulticast: Failed to open socket: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to open socket")); + } + + // send a message to the server + if (sendto(socketFD, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) + { + int errCode = errno; + std::cerr << "LinHttpHandler: sendMulticast: Failed to send message: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to send message")); + } + + std::string response; + char buffer[2048] = {}; // receive buffer + ssize_t bytesReceived = 0; + + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) + { + bytesReceived = recv(socketFD, &buffer, 2048, MSG_DONTWAIT); + if (bytesReceived < 0) + { + int errCode = errno; + if (errCode != EAGAIN && errCode != EWOULDBLOCK) + { + std::cerr << "LinHttpHandler: sendMulticast: Failed to read response from socket: " << std::strerror(errCode) << "\n"; + throw(std::system_error(errCode, std::generic_category(), "LinHttpHandler: sendMulticast: Failed to read response from socket")); + } + continue; + } + if (bytesReceived) + { + response.append(buffer, bytesReceived); + } + } + + // construct return vector + std::vector returnString; + size_t pos = response.find("\r\n\r\n"); + size_t prevpos = 0; + while (pos != std::string::npos) + { + returnString.push_back(response.substr(prevpos, pos - prevpos)); + pos += 4; + prevpos = pos; + pos = response.find("\r\n\r\n", pos); + } + return returnString; } diff --git a/hueplusplus/WinHttpHandler.cpp b/hueplusplus/WinHttpHandler.cpp index ef403ab..dca1a9c 100755 --- a/hueplusplus/WinHttpHandler.cpp +++ b/hueplusplus/WinHttpHandler.cpp @@ -23,239 +23,273 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include #include #pragma comment(lib, "Ws2_32.lib") +namespace +{ + class AddrInfoFreer + { + public: + explicit AddrInfoFreer(addrinfo* p) : p(p) + {} + ~AddrInfoFreer() + { + freeaddrinfo(p); + } + private: + addrinfo * p; + }; + class SocketCloser + { + public: + explicit SocketCloser(SOCKET s) : s(s) + {} + ~SocketCloser() + { + closesocket(s); + } + private: + SOCKET s; + }; +} + WinHttpHandler::WinHttpHandler() { - // Initialize Winsock - int return_code = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (return_code != 0) - { - std::cerr << "WinHttpHandler: Failed to open socket: " << return_code << std::endl; - throw(std::runtime_error("WinHttpHandler: Failed to open socket")); - } + // Initialize Winsock + int return_code = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (return_code != 0) + { + std::cerr << "WinHttpHandler: Failed to open socket: " << return_code << std::endl; + throw(std::system_error(return_code, std::system_category(), "WinHttpHandler: Failed to open socket")); + } } WinHttpHandler::~WinHttpHandler() { - WSACleanup(); + WSACleanup(); } std::string WinHttpHandler::send(const std::string & msg, const std::string & adr, int port) const { - struct addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - // Resolve the server address and port - struct addrinfo *result = nullptr; - if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) - { - std::cerr << "WinHttpHandler: getaddrinfo failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: getaddrinfo failed")); - } - - // Attempt to connect to the first address returned by - // the call to getaddrinfo - struct addrinfo *ptr = result; - - // Create a SOCKET for connecting to server - SOCKET connect_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); - - if (connect_socket == INVALID_SOCKET) - { - freeaddrinfo(result); - std::cerr << "WinHttpHandler: Error at socket(): " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: Error at socket()")); - } - - // Connect to server. - if (connect(connect_socket, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR) - { - closesocket(connect_socket); - connect_socket = INVALID_SOCKET; - } - - // Should really try the next address returned by getaddrinfo - // if the connect call failed - // But for this simple example we just free the resources - // returned by getaddrinfo and print an error message - - freeaddrinfo(result); - - if (connect_socket == INVALID_SOCKET) - { - std::cerr << "WinHttpHandler: Unable to connect to server!" << std::endl; - throw(std::runtime_error("WinHttpHandler: Unable to connect to server!")); - } - - // Send an initial buffer - if (::send(connect_socket, msg.c_str(), msg.size(), 0) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: send failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: send failed")); - } - - // shutdown the connection for sending since no more data will be sent - // the client can still use the ConnectSocket for receiving data - if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: shutdown failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: shutdown failed")); - } - - const int recvbuflen = 128; - char recvbuf[recvbuflen]; - - // Receive data until the server closes the connection - std::string response; - int res; - do - { - res = recv(connect_socket, recvbuf, recvbuflen, 0); - if (res > 0) - { - //std::cout << "WinHttpHandler: Bytes received: " << res << std::endl; - response.append(recvbuf, res); - } - else if (res == 0) - { - //std::cout << "WinHttpHandler: Connection closed " << std::endl; - } - else - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: recv failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: recv failed")); - } - } while (res > 0); - closesocket(connect_socket); - - return response; + struct addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + struct addrinfo *result = nullptr; + if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: getaddrinfo failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: getaddrinfo failed")); + } + SOCKET connect_socket = INVALID_SOCKET; + { + AddrInfoFreer freeResult(result); + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + struct addrinfo *ptr = result; + + // Create a SOCKET for connecting to server + connect_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + + if (connect_socket == INVALID_SOCKET) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: Error at socket(): " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: Error at socket()")); + } + + // Connect to server. + if (connect(connect_socket, ptr->ai_addr, (int)ptr->ai_addrlen) == SOCKET_ERROR) + { + closesocket(connect_socket); + connect_socket = INVALID_SOCKET; + } + + // Should really try the next address returned by getaddrinfo + // if the connect call failed + // But for this simple example we just free the resources + // returned by getaddrinfo and print an error message + } + + if (connect_socket == INVALID_SOCKET) + { + std::cerr << "WinHttpHandler: Unable to connect to server!" << std::endl; + throw(std::runtime_error("WinHttpHandler: Unable to connect to server!")); + } + SocketCloser closeSocket(connect_socket); + + // Send an initial buffer + if (::send(connect_socket, msg.c_str(), msg.size(), 0) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: send failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: send failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: shutdown failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: shutdown failed")); + } + + const int recvbuflen = 128; + char recvbuf[recvbuflen]; + + // Receive data until the server closes the connection + std::string response; + int res; + do + { + res = recv(connect_socket, recvbuf, recvbuflen, 0); + if (res > 0) + { + //std::cout << "WinHttpHandler: Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + //std::cout << "WinHttpHandler: Connection closed " << std::endl; + } + else + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: recv failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: recv failed")); + } + } while (res > 0); + + return response; } std::vector WinHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const { - struct addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_TCP; - - // Resolve the server address and port - struct addrinfo *result = nullptr; - if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) - { - std::cerr << "WinHttpHandler: sendMulticast: getaddrinfo failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: getaddrinfo failed")); - } - - // Attempt to connect to the first address returned by - // the call to getaddrinfo - struct addrinfo *ptr = result; - - // Create a SOCKET for connecting to server - SOCKET connect_socket; - if ((connect_socket = socket(ptr->ai_family, ptr->ai_socktype, 0)) == INVALID_SOCKET) - { - freeaddrinfo(result); - std::cerr << "WinHttpHandler: sendMulticast: Error at socket(): " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: Error at socket()")); - } - - // Fill out source socket's address information. - SOCKADDR_IN source_sin; - source_sin.sin_family = AF_INET; - source_sin.sin_port = htons(0); - source_sin.sin_addr.s_addr = htonl(INADDR_ANY); - - // Associate the source socket's address with the socket, Sock. - if (bind(connect_socket, (struct sockaddr FAR *) &source_sin, sizeof(source_sin)) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: sendMulticast: Binding socket failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: Binding socket failed")); - } - - u_long sock_mode = 1; - ioctlsocket(connect_socket, FIONBIO, &sock_mode); - - BOOL bOptVal = TRUE; - setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, sizeof(bOptVal)); - - // Set the Time-to-Live of the multicast. - int iOptVal = 1; // for same subnet, but might be increased to 16 - if (setsockopt(connect_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char FAR *)&iOptVal, sizeof(int)) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: sendMulticast: setsockopt failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: setsockopt failed")); - } - - // Fill out the desination socket's address information. - SOCKADDR_IN dest_sin; - dest_sin.sin_family = AF_INET; - dest_sin.sin_port = htons(port); - dest_sin.sin_addr.s_addr = inet_addr((const char*)ptr->ai_addr); - - // Send a message to the multicasting address. - if (sendto(connect_socket, msg.c_str(), msg.size(), 0, (struct sockaddr FAR *) &dest_sin, sizeof(dest_sin)) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: sendMulticast: sendto failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: sendto failed")); - } - - // shutdown the connection for sending since no more data will be sent - // the client can still use the ConnectSocket for receiving data - if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "WinHttpHandler: sendMulticast: shutdown failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("WinHttpHandler: sendMulticast: shutdown failed")); - } - - std::string response; - const int recvbuflen = 2048; - char recvbuf[recvbuflen] = {}; - int res; - std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); - while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) - { - res = recv(connect_socket, recvbuf, recvbuflen, 0); - if (res > 0) - { - //std::cout << "WinHttpHandler: sendMulticast: Bytes received: " << res << std::endl; - response.append(recvbuf, res); - } - else if (res == 0) - { - //std::cout << "WinHttpHandler: sendMulticast: Connection closed " << std::endl; - } - else - { - // No exception here due to non blocking socket - //std::cerr << "sendMulticast: recv failed: " << WSAGetLastError() << std::endl; - //throw(std::runtime_error("recv failed")); - } - } - closesocket(connect_socket); // Is this needed? - - // construct return vector - std::vector returnString; - size_t pos = response.find("\r\n\r\n"); - size_t prevpos = 0; - while (pos != std::string::npos) - { - returnString.push_back(response.substr(prevpos, pos - prevpos)); - pos += 4; - prevpos = pos; - pos = response.find("\r\n\r\n", pos); - } - - return returnString; + struct addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + struct addrinfo *result = nullptr; + if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: getaddrinfo failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: getaddrinfo failed")); + } + AddrInfoFreer freeResult(result); + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + struct addrinfo *ptr = result; + + // Create a SOCKET for connecting to server + SOCKET connect_socket = socket(ptr->ai_family, ptr->ai_socktype, 0); + if (connect_socket == INVALID_SOCKET) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: Error at socket(): " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: Error at socket()")); + } + SocketCloser closeSocket(connect_socket); + + // Fill out source socket's address information. + SOCKADDR_IN source_sin; + source_sin.sin_family = AF_INET; + source_sin.sin_port = htons(0); + source_sin.sin_addr.s_addr = htonl(INADDR_ANY); + + // Associate the source socket's address with the socket, Sock. + if (bind(connect_socket, (struct sockaddr FAR *) &source_sin, sizeof(source_sin)) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: Binding socket failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: Binding socket failed")); + } + + u_long sock_mode = 1; + ioctlsocket(connect_socket, FIONBIO, &sock_mode); + + BOOL bOptVal = TRUE; + setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, sizeof(bOptVal)); + + // Set the Time-to-Live of the multicast. + int iOptVal = 1; // for same subnet, but might be increased to 16 + if (setsockopt(connect_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char FAR *)&iOptVal, sizeof(int)) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: setsockopt failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: setsockopt failed")); + } + + // Fill out the desination socket's address information. + SOCKADDR_IN dest_sin; + dest_sin.sin_family = AF_INET; + dest_sin.sin_port = htons(port); + dest_sin.sin_addr.s_addr = inet_addr((const char*)ptr->ai_addr); + + // Send a message to the multicasting address. + if (sendto(connect_socket, msg.c_str(), msg.size(), 0, (struct sockaddr FAR *) &dest_sin, sizeof(dest_sin)) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: sendto failed: " << WSAGetLastError() << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: sendto failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) + { + int err = WSAGetLastError(); + std::cerr << "WinHttpHandler: sendMulticast: shutdown failed: " << err << std::endl; + throw(std::system_error(err, std::system_category(), "WinHttpHandler: sendMulticast: shutdown failed")); + } + + std::string response; + const int recvbuflen = 2048; + char recvbuf[recvbuflen] = {}; + int res; + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) + { + res = recv(connect_socket, recvbuf, recvbuflen, 0); + if (res > 0) + { + //std::cout << "WinHttpHandler: sendMulticast: Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + //std::cout << "WinHttpHandler: sendMulticast: Connection closed " << std::endl; + } + else + { + // No exception here due to non blocking socket + //std::cerr << "sendMulticast: recv failed: " << WSAGetLastError() << std::endl; + //throw(std::runtime_error("recv failed")); + } + } + + // construct return vector + std::vector returnString; + size_t pos = response.find("\r\n\r\n"); + size_t prevpos = 0; + while (pos != std::string::npos) + { + returnString.push_back(response.substr(prevpos, pos - prevpos)); + pos += 4; + prevpos = pos; + pos = response.find("\r\n\r\n", pos); + } + + return returnString; }