I was trying to write some C++ code to control my Philips Hue lights. After using the browser debug tool to figure out my bridge's IP and adding a user to control the lights with, I tried to replicate the messages sent by my browser in code to create my own routines.
This is the code I'm using:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define PORT "80"
int main() {
const char* adress = "192.168.178.x";
// Initializing Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); //MAKEWORD(2, 2) specifies the version
if (iResult != 0)
{
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// Data structure to hold info for the socket
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // Only using IPv4 adresses
hints.ai_socktype = SOCK_STREAM; // TCP socket
hints.ai_protocol = IPPROTO_TCP;
// Converting adress to readable format and stuffing it into result.
iResult = getaddrinfo(adress, PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// Actually creating a socket
SOCKET ConnectSocket = INVALID_SOCKET;
ptr = result;
// Creating socket with previously initialized data
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Connecting to the server socket
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
// Error checking
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
/////////////////////////////////////////////////////////
// ACTUAL MESSAGE
std::string request = "GET /api/<username>/lights/2 HTTP/1.1\r\n";
request.append("Host: 192.168.178.x\r\n");
request.append("\r\n");
////////////////////////////////////////////////////////
printf("Request:\n%s", request.c_str());
// Send message
iResult = send(ConnectSocket, request.c_str(),(int) request.size(), 0);
// Error check
if (iResult == SOCKET_ERROR)
{
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// Shutting down socket since it won't be used anymore (can still receive data)
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
const int buflen = 8196;
char recvbuf[buflen];
ZeroMemory(recvbuf, buflen);
// Keep socket open until connection is closed
do
{
iResult = recv(ConnectSocket, recvbuf, buflen, 0);
if (iResult > 0)
{
printf("Bytes received: %d\n", iResult);
printf("\nResponse:\n%s\n", recvbuf);
}
else if (iResult == 0)
{
printf("Connection closed\n");
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
}
} while (iResult > 0);
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
If you look at the edit history of this question, you will see that I previously thought that something was wrong with my HTTP messages but I found some weird behavior:
After every reboot of my system I get a proper response the first time I sent my message. But all following attempts result in an immediate closed connection.
Still no clue what's causing this, but I guess something isn't closed or terminated properly?
After some trial and error I found something that made it work.
If I put this part:
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR)
{
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
after the part that is receiving messages it works.
My guess is that although the documentation says that the socket can still receive messages after being shut down, it changes something that makes the server think that the connection should be closed.
No idea whether that is correct though.
If somebody knows the actual reason then feel free to add a comment.
I'm just happy I don't have to deal with this anymore.