IWebSocketClient.h
Code: Select all
#ifndef _I_WEB_SOCKET_CLIENT
#define _I_WEB_SOCKET_CLIENT
#include <irrlicht.h>
class IWebSocketClient {
public:
enum enWsState {
enWsUninitialized,
enWsInitialized,
enWsConnecting,
enWsConnected,
enWsDisconnected
};
private:
void handleData(const char *a_sData);
protected:
unsigned int m_iSocket;
irr::core::stringc m_sProtocol,
m_sServer,
m_sPage;
time_t m_iLastPing;
enWsState m_iState;
unsigned short m_iPort;
void parseUrl(const irr::c8 *a_sUrl);
int connectToServer() ;
virtual bool dataReceived(const char *a_sData) = 0;
irr::u32 parseHeader(const irr::c8 *a_sData, irr::u32 &a_iLength, irr::u8 &a_iOpcode, bool &a_bFin, bool &a_bMask);
void sendData(const irr::c8 *a_sBuffer, irr::s32 a_iLen = -1);
public:
IWebSocketClient();
int getSocket();
virtual void connectToServer(const irr::c8 *a_sUrl);
void update();
enWsState getState();
void sendText(const irr::c8 *a_sInput);
};
#endif
Code: Select all
#include <time.h>
#include <winsock2.h>
#include <net-o-matic.h>
#include <IWebSocketClient.h>
#include <irrlicht.h>
void IWebSocketClient::handleData(const char *a_sData) {
switch (m_iState) {
case enWsConnecting:
if (strstr(a_sData, "Switching Protocols") &&
strstr(a_sData, "Upgrade: websocket" ) &&
strstr(a_sData, "Connection: Upgrade")) { printf("## Connection established.\n"); m_iState = enWsConnected; }
break;
case enWsConnected:
if (!dataReceived(a_sData)) {
//printf("--> %s\n", a_sData);
}
break;
}
}
void IWebSocketClient::parseUrl(const irr::c8 *a_sUrl) {
irr::core::stringc l_sUrl = irr::core::stringc(a_sUrl),
l_sDummy;
m_iPort = 80;
if (l_sUrl.find("://") != -1) m_sProtocol = l_sUrl.subString(0, l_sUrl.find("://"));
m_sPage = l_sUrl.subString(l_sUrl.find("://") + 3, l_sUrl.size());
if (m_sPage.find(":") != -1) {
m_sServer = m_sPage.subString(0, m_sPage.find(":"));
l_sDummy = m_sPage.subString(m_sPage.find(":") + 1, l_sUrl.size());
if (l_sDummy.find("/") != -1) {
l_sDummy = l_sDummy.subString(0, l_sDummy.find("/"));
}
m_sPage = m_sPage.subString(m_sPage.find("/"), m_sPage.size());
m_iPort = atoi(l_sDummy.c_str());
}
else {
if (m_sPage.find("/") != -1) {
m_sServer = m_sPage.subString(0, m_sPage.find("/"));
m_sPage = m_sPage.subString(m_sPage.find("/") + 1, m_sPage.size());
}
else {
m_sServer = m_sPage;
m_sPage = "/";
}
}
printf("## parseUrl: server: \"%s\", port: %i, page: \"%s\"\n", m_sServer.c_str(), m_iPort, m_sPage.c_str());
}
int IWebSocketClient::connectToServer() {
unsigned long l_ulIAdd,
l_uArg=0;
struct hostent *l_pHost;
struct sockaddr_in l_cAdd;
if (m_iSocket == INVALID_SOCKET || m_iPort > (unsigned short)0xFFFF) return -1;
memset(&l_cAdd,0,sizeof(struct sockaddr_in));
l_cAdd.sin_family = AF_INET;
l_cAdd.sin_port = htons(m_iPort);
l_ulIAdd = inet_addr(m_sServer.c_str());
if (l_ulIAdd == INADDR_NONE)
{
l_pHost=gethostbyname(m_sServer.c_str());
if (!l_pHost) return -1;
l_cAdd.sin_addr.s_addr = *((int *)l_pHost->h_addr_list[0]);
}
else l_cAdd.sin_addr.s_addr=l_ulIAdd;
l_uArg = connect(m_iSocket,(struct sockaddr *)&l_cAdd,sizeof(l_cAdd));
printf("## connection returned %i (%i)\n", l_uArg, WSAGetLastError());
if (l_uArg != 0) return -3;
u_long l_iMode=1;
ioctlsocket(m_iSocket,FIONBIO,&l_iMode);
return 1;
}
IWebSocketClient::IWebSocketClient() {
int l_iProtocol = IPPROTO_TCP,
l_iErr;
WSADATA l_cWsa;
m_iSocket = -1;
m_iLastPing = time(NULL);
l_iErr = WSAStartup(MAKEWORD(2,2),&l_cWsa);
if (l_iErr && l_iErr != WSAVERNOTSUPPORTED) return;
m_iSocket = socket(PF_INET, SOCK_STREAM, l_iProtocol);
printf("## socket: %i\n", m_iSocket);
m_iState = enWsInitialized;
}
int IWebSocketClient::getSocket() {
return m_iSocket;
}
void IWebSocketClient::sendData(const irr::c8 *a_sBuffer, irr::s32 a_iLen) {
send(m_iSocket, a_sBuffer, a_iLen == -1 ? strlen(a_sBuffer) : a_iLen, 0);
}
void IWebSocketClient::connectToServer(const irr::c8 *a_sUrl) {
irr::core::stringc l_sRequest = "";
parseUrl(a_sUrl);
connectToServer();
l_sRequest = "GET /";
l_sRequest += m_sPage;
l_sRequest += "\r\n";
l_sRequest += "Accent: */*\r\n";
l_sRequest += "User-Agent: webClient/2.0 http://www.bulletbyte.de\r\n";
l_sRequest += "Host: ";
l_sRequest += m_sServer;
l_sRequest += "\r\n";
l_sRequest += "Upgrade: websocket\r\n";
l_sRequest += "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n";
l_sRequest += "Sec-WebSocket-Version: 13\r\n";
l_sRequest += "Connection: Upgrade\r\n\r\n";
sendData(l_sRequest.c_str());
m_iState = enWsConnecting;
}
irr::u32 IWebSocketClient::parseHeader(const irr::c8 *a_sData, irr::u32 &a_iLength, irr::u8 &a_iOpcode, bool &a_bFin, bool &a_bMask) {
a_bFin = (a_sData[0] & 128) != 0;
a_bMask = (a_sData[1] & 128) != 0;
a_iOpcode = a_sData[0] & 15;
a_iLength = a_sData[1] & 127;
if (a_iLength == 126) {
a_iLength = ntohs(*((irr::u16 *)&a_sData[2]));
return 4;
}
else
if (a_iLength == 127) {
a_iLength = (irr::u32)htonll(*((unsigned long long *)&a_sData[2]));
return 10;
}
return 2;
}
void IWebSocketClient::update() {
char l_sBuffer[0xFFFF] = "",
l_sDummy [0xFFFF] = "";
irr::s32 l_iDummy;
do {
memset(l_sBuffer, 0, 0xFFFF);
memset(l_sDummy , 0, 0xFFFF);
l_iDummy = recv(m_iSocket, (char *)l_sBuffer, 0xFFFF, 0);
int l_iError=WSAGetLastError();
if(l_iError != WSAEWOULDBLOCK && l_iError!=0) {
m_iState = enWsDisconnected;
}
if (l_iDummy > 0) {
if ((unsigned char)(l_sBuffer[0] & 9) == 9) {
unsigned char s[2] = { 137, 0 };
sendData((const char *)s, 2);
}
if (m_iLastPing + 2 < time(NULL)) {
unsigned char s[10] = { 0x8a, 0x80, 0x51, 0x7c, 0xc3, 0xf1, 0x00 };
sendData((const char *)s, 6);
m_iLastPing = time(NULL);
}
irr::s32 l_iCurrentPos = 0;
while (strlen(l_sBuffer) > 0 && l_iCurrentPos < l_iDummy) {
switch (m_iState) {
case enWsConnecting: {
char *s = strstr(l_sBuffer, "\r\n\r\n");
if (s != NULL) {
*s = '\0'; s++;
handleData(l_sBuffer);
strcpy_s(l_sBuffer, &s[3]);
}
}
break;
case enWsConnected: {
bool l_bFin,
l_bMask;
irr::u8 l_iOpcode;
irr::u32 l_iLength,
l_iDataStart = parseHeader(&l_sBuffer[l_iCurrentPos], l_iLength, l_iOpcode, l_bFin, l_bMask);
l_iCurrentPos += l_iDataStart;
handleData(&l_sBuffer[l_iCurrentPos]);
l_iCurrentPos += l_iLength;
}
break;
}
}
//handleData(&l_sBuffer[iOld]);
}
}
while (l_iDummy >= 0);
}
IWebSocketClient::enWsState IWebSocketClient::getState() {
return m_iState;
}
void IWebSocketClient::sendText(const char *a_sInput) {
char l_sBuffer[0xFFFF];
irr::u16 l_iHeaderSize;
// Setting the first byte of the header: fin flag + "text frame" opcode
l_sBuffer[0] = 128 + 1;
// Easy version: length of the string to be transmitted less than 126
if (strlen(a_sInput) < 126) {
l_sBuffer[1] = strlen(a_sInput);
strcpy_s(&l_sBuffer[2], 0xFFFF - 2, a_sInput);
l_iHeaderSize = 2;
}
else
// Compilcated version: up to 0xFFFB bytes (0xFFFF - length of header)
if (strlen(a_sInput) <= 0xFFFB) {
l_sBuffer[1] = 126;
*((unsigned short *)&l_sBuffer[2]) = htons(strlen(a_sInput));
strcpy_s(&l_sBuffer[4], 0xFFFF - 4, a_sInput);
l_iHeaderSize = 4;
}
else {
// This won't work. At the moment the implementation is not able to send frames bigger than 0xFFFB bytes
l_sBuffer[1] = 127;
*((u_int64 *)&l_sBuffer[4]) = htonll(strlen(a_sInput));
strcpy_s(&l_sBuffer[8], 0xFFFF - 8, a_sInput);
l_iHeaderSize = 8;
}
send(m_iSocket, l_sBuffer, l_iHeaderSize + strlen(a_sInput), 0);
}