diff --git a/.gitignore b/.gitignore index 9cd7b1b..bf4a78d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,155 @@ *.a node_modules/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store diff --git a/easywsclient.cpp b/easywsclient.cpp index e293fbe..7f30dab 100644 --- a/easywsclient.cpp +++ b/easywsclient.cpp @@ -1,28 +1,75 @@ #include "easywsclient.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #include + #pragma comment( lib, "ws2_32" ) + #include + #include + #include + #include + #include + #ifndef _SSIZE_T_DEFINED + typedef int ssize_t; + #define _SSIZE_T_DEFINED + #endif + #ifndef _SOCKET_T_DEFINED + typedef SOCKET socket_t; + #define _SOCKET_T_DEFINED + #endif + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #if _MSC_VER >=1600 + // vs2010 or later + #include + #else + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #endif +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #ifndef _SOCKET_T_DEFINED + typedef int socket_t; + #define _SOCKET_T_DEFINED + #endif + #ifndef INVALID_SOCKET + #define INVALID_SOCKET (-1) + #endif + #ifndef SOCKET_ERROR + #define SOCKET_ERROR (-1) + #endif +#endif #include #include namespace { // private module-only namespace -int hostname_connect(std::string hostname, int port) { +socket_t hostname_connect(std::string hostname, int port) { struct addrinfo hints; struct addrinfo *result; struct addrinfo *p; int ret; - int sockfd = -1; + socket_t sockfd = INVALID_SOCKET; char sport[16]; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; @@ -36,12 +83,16 @@ int hostname_connect(std::string hostname, int port) { for(p = result; p != NULL; p = p->ai_next) { sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (sockfd == -1) { continue; } - if (connect(sockfd, p->ai_addr, p->ai_addrlen) != -1) { + if (sockfd == INVALID_SOCKET) { continue; } + if (connect(sockfd, p->ai_addr, p->ai_addrlen) != SOCKET_ERROR) { break; } +#ifdef _WIN32 + closesocket(sockfd); +#else close(sockfd); - sockfd = -1; +#endif + sockfd = INVALID_SOCKET; } freeaddrinfo(result); return sockfd; @@ -100,13 +151,21 @@ class _RealWebSocket : public easywsclient::WebSocket uint8_t masking_key[4]; }; + inline int close(socket_t sockfd) { +#ifdef _WIN32 + return ::closesocket(sockfd); +#else + return ::close(sockfd); +#endif + } + std::vector rxbuf; std::vector txbuf; - int sockfd; + socket_t sockfd; readyStateValues readyState; - _RealWebSocket(int sockfd) : sockfd(sockfd), readyState(OPEN) { + _RealWebSocket(socket_t sockfd) : sockfd(sockfd), readyState(OPEN) { } readyStateValues getReadyState() { @@ -120,7 +179,11 @@ class _RealWebSocket : public easywsclient::WebSocket int N = rxbuf.size(); ssize_t ret; rxbuf.resize(N + 1500); +#ifdef _WIN32 + ret = recv(sockfd, (int8_t*)&rxbuf[0] + N, 1500, 0); +#else ret = recv(sockfd, &rxbuf[0] + N, 1500, 0); +#endif if (false) { } else if (ret < 0) { rxbuf.resize(N); @@ -128,7 +191,7 @@ class _RealWebSocket : public easywsclient::WebSocket } else if (ret == 0) { rxbuf.resize(N); - ::close(sockfd); + close(sockfd); readyState = CLOSED; fprintf(stderr, "Connection closed!\n"); break; @@ -139,12 +202,16 @@ class _RealWebSocket : public easywsclient::WebSocket } while (txbuf.size()) { int ret; +#ifdef _WIN32 + ret = ::send(sockfd, (int8_t*)&txbuf[0], txbuf.size(), 0); +#else ret = ::send(sockfd, &txbuf[0], txbuf.size(), 0); +#endif if (ret > 0) { txbuf.erase(txbuf.begin(), txbuf.begin() + ret); } else { break; } } if (!txbuf.size() && readyState == CLOSING) { - ::close(sockfd); + close(sockfd); readyState = CLOSED; } } @@ -223,27 +290,28 @@ class _RealWebSocket : public easywsclient::WebSocket // TODO: consider acquiring a lock on txbuf... if (readyState == CLOSING || readyState == CLOSED) { return; } std::vector header; - header.assign(2 + (message.size() >= 126 ? 2 : 0) + (message.size() >= 65536 ? 6 : 0), 0); + uint64_t message_size = message.size(); + header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0), 0); header[0] = 0x80 | wsheader_type::TEXT_FRAME; if (false) { } - else if (message.size() < 126) { - header[1] = message.size(); + else if (message_size < 126) { + header[1] = message_size & 0xff; } - else if (message.size() < 65536) { + else if (message_size < 65536) { header[1] = 126; - header[2] = (message.size() >> 8) & 0xff; - header[3] = (message.size() >> 0) & 0xff; + header[2] = (message_size >> 8) & 0xff; + header[3] = (message_size >> 0) & 0xff; } else { // TODO: run coverage testing here header[1] = 127; - header[2] = (message.size() >> 56) & 0xff; - header[3] = (message.size() >> 48) & 0xff; - header[4] = (message.size() >> 40) & 0xff; - header[5] = (message.size() >> 32) & 0xff; - header[6] = (message.size() >> 24) & 0xff; - header[7] = (message.size() >> 16) & 0xff; - header[8] = (message.size() >> 8) & 0xff; - header[9] = (message.size() >> 0) & 0xff; + header[2] = (message_size >> 56) & 0xff; + header[3] = (message_size >> 48) & 0xff; + header[4] = (message_size >> 40) & 0xff; + header[5] = (message_size >> 32) & 0xff; + header[6] = (message_size >> 24) & 0xff; + header[7] = (message_size >> 16) & 0xff; + header[8] = (message_size >> 8) & 0xff; + header[9] = (message_size >> 0) & 0xff; } txbuf.insert(txbuf.end(), header.begin(), header.end()); txbuf.insert(txbuf.end(), message.begin(), message.end()); @@ -294,8 +362,8 @@ WebSocket::pointer WebSocket::from_url(std::string url) { return NULL; } fprintf(stderr, "easywsclient: connecting: host=%s port=%d path=/%s\n", host, port, path); - int sockfd = hostname_connect(host, port); - if (sockfd == -1) { + socket_t sockfd = hostname_connect(host, port); + if (sockfd == INVALID_SOCKET) { fprintf(stderr, "Unable to connect to %s:%d\n", host, port); return NULL; } @@ -323,7 +391,12 @@ WebSocket::pointer WebSocket::from_url(std::string url) { } int flag = 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm +#ifdef _WIN32 + u_long on = 1; + ioctlsocket(sockfd, FIONBIO, &on); +#else fcntl(sockfd, F_SETFL, O_NONBLOCK); +#endif fprintf(stderr, "Connected to: %s\n", url.c_str()); return pointer(new _RealWebSocket(sockfd)); } diff --git a/example-client.cpp b/example-client.cpp index 5fe1884..abc8159 100644 --- a/example-client.cpp +++ b/example-client.cpp @@ -1,5 +1,9 @@ #include "easywsclient.hpp" //#include "easywsclient.cpp" // <-- include only if you don't want compile separately +#ifdef _WIN32 +#pragma comment( lib, "ws2_32" ) +#include +#endif #include #include #include @@ -15,6 +19,17 @@ void handle_message(const std::string & message) int main() { +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) { + printf("WSAStartup Failed.\n"); + return 1; + } +#endif + ws = WebSocket::from_url("ws://localhost:8126/foo"); assert(ws); ws->send("goodbye"); @@ -24,5 +39,8 @@ int main() ws->dispatch(handle_message); } delete ws; +#ifdef _WIN32 + WSACleanup(); +#endif return 0; }