From 71b40c6d6ca3279afd93ab790808c28c264913ae Mon Sep 17 00:00:00 2001 From: Benjamin Sergeant Date: Mon, 8 Oct 2018 21:42:45 -0700 Subject: [PATCH] Windows support (no TLS yet) --- README.md | 4 +- examples/chat/cmd_websocket_chat.cpp | 19 +++-- ixwebsocket/IXSocket.cpp | 105 ++++++++++++++++++++------- ixwebsocket/IXSocket.h | 19 ++++- ixwebsocket/IXWebSocketTransport.cpp | 14 ++-- 5 files changed, 116 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 40e8c7d7..c6eb38fd 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,8 @@ If the remote end (server) breaks the connection, the code will try to perpetual 2. Compile the example C++ code. `sh build.sh` 3. Install node.js from [here](https://nodejs.org/en/download/). 4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server. -5. Bring up a second terminal. `env USER=bob ./cmd_websocket_chat` -6. Bring up a third terminal. `env USER=bill ./cmd_websocket_chat` +5. Bring up a second terminal. `./cmd_websocket_chat bob` +6. Bring up a third terminal. `./cmd_websocket_chat bill` 7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end. ## C++ code organization diff --git a/examples/chat/cmd_websocket_chat.cpp b/examples/chat/cmd_websocket_chat.cpp index b52d9a19..70e29f63 100644 --- a/examples/chat/cmd_websocket_chat.cpp +++ b/examples/chat/cmd_websocket_chat.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "nlohmann/json.hpp" @@ -158,13 +159,10 @@ namespace _webSocket.send(encodeMessage(text)); } - void interactiveMain() + void interactiveMain(const std::string& user) { - std::string user(getenv("USER")); - - WebSocketChat webSocketChat(user); - std::cout << "Type Ctrl-D to exit prompt..." << std::endl; + WebSocketChat webSocketChat(user); webSocketChat.start(); while (true) @@ -186,8 +184,15 @@ namespace } } -int main() +int main(int argc, char** argv) { - interactiveMain(); + std::string user("user"); + if (argc == 2) + { + user = argv[1]; + } + + Socket::init(); + interactiveMain(user); return 0; } diff --git a/ixwebsocket/IXSocket.cpp b/ixwebsocket/IXSocket.cpp index 3917100d..5fbf1895 100644 --- a/ixwebsocket/IXSocket.cpp +++ b/ixwebsocket/IXSocket.cpp @@ -6,22 +6,32 @@ #include "IXSocket.h" -#include -#include +#ifdef _WIN32 +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +#endif + #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include + +#include // // Linux/Android has a special type of virtual files. select(2) will react @@ -35,7 +45,7 @@ // cf Android/Kernel table here // https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel // -#ifndef __APPLE__ +#ifdef __linux__ # include #endif @@ -51,7 +61,7 @@ namespace ix _sockfd(-1), _eventfd(-1) { -#ifndef __APPLE__ +#ifdef __linux__ _eventfd = eventfd(0, 0); assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform"); #endif @@ -61,14 +71,14 @@ namespace ix { close(); -#ifndef __APPLE__ +#ifdef __linux__ ::close(_eventfd); #endif } - bool connectToAddress(const struct addrinfo *address, - int& sockfd, - std::string& errMsg) + bool Socket::connectToAddress(const struct addrinfo *address, + int& sockfd, + std::string& errMsg) { sockfd = -1; @@ -84,7 +94,7 @@ namespace ix int maxRetries = 3; for (int i = 0; i < maxRetries; ++i) { - if (connect(fd, address->ai_addr, address->ai_addrlen) != -1) + if (::connect(fd, address->ai_addr, address->ai_addrlen) != -1) { sockfd = fd; return true; @@ -94,7 +104,7 @@ namespace ix if (errno != EINTR) break; } - ::close(fd); + closeSocket(fd); sockfd = -1; errMsg = strerror(errno); return false; @@ -142,7 +152,13 @@ namespace ix { int flag = 1; setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm + +#ifdef _WIN32 + unsigned long nonblocking = 1; + ioctlsocket(_sockfd, FIONBIO, &nonblocking); +#else fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking +#endif #ifdef SO_NOSIGPIPE int value = 1; @@ -163,12 +179,12 @@ namespace ix FD_ZERO(&rfds); FD_SET(_sockfd, &rfds); -#ifndef __APPLE__ +#ifdef __linux__ FD_SET(_eventfd, &rfds); #endif int sockfd = _sockfd; - int nfds = std::max(sockfd, _eventfd); + int nfds = (std::max)(sockfd, _eventfd); select(nfds + 1, &rfds, nullptr, nullptr, nullptr); onPollCallback(); @@ -191,7 +207,7 @@ namespace ix { #ifdef __APPLE__ wakeUpFromPollApple(); -#else +#elif defined(__linux__) wakeUpFromPollLinux(); #endif } @@ -202,7 +218,7 @@ namespace ix { std::lock_guard lock(_socketMutex); -#ifndef __APPLE__ +#ifdef __linux__ if (_eventfd == -1) { return false; // impossible to use this socket if eventfd is broken @@ -219,7 +235,7 @@ namespace ix if (_sockfd == -1) return; - ::close(_sockfd); + closeSocket(_sockfd); _sockfd = -1; } @@ -245,7 +261,44 @@ namespace ix flags = MSG_NOSIGNAL; #endif - return (int) ::recv(_sockfd, buffer, length, flags); + return (int) ::recv(_sockfd, (char*) buffer, length, flags); } + int Socket::getErrno() const + { +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif + } + + void Socket::closeSocket(int fd) + { +#ifdef _WIN32 + closesocket(fd); +#else + ::close(fd); +#endif + } + + bool Socket::init() + { +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + return rc != 0; +#else + return true; +#endif + } + + void Socket::cleanup() + { +#ifdef _WIN32 + WSACleanup(); +#endif + } } diff --git a/ixwebsocket/IXSocket.h b/ixwebsocket/IXSocket.h index 8084e219..adb812dc 100644 --- a/ixwebsocket/IXSocket.h +++ b/ixwebsocket/IXSocket.h @@ -11,6 +11,8 @@ #include #include +struct addrinfo; + namespace ix { class Socket { @@ -20,9 +22,9 @@ namespace ix Socket(); virtual ~Socket(); - static int hostname_connect(const std::string& hostname, - int port, - std::string& errMsg); + int hostname_connect(const std::string& hostname, + int port, + std::string& errMsg); void configure(); virtual void poll(const OnPollCallback& onPollCallback); @@ -38,13 +40,24 @@ namespace ix virtual int send(const std::string& buffer); virtual int recv(void* buffer, size_t length); + int getErrno() const; + static bool init(); // Required on Windows to initialize WinSocket + static void cleanup(); // Required on Windows to cleanup WinSocket + protected: void wakeUpFromPollApple(); void wakeUpFromPollLinux(); + void closeSocket(int fd); + std::atomic _sockfd; int _eventfd; std::mutex _socketMutex; + + private: + bool connectToAddress(const struct addrinfo *address, + int& sockfd, + std::string& errMsg); }; } diff --git a/ixwebsocket/IXWebSocketTransport.cpp b/ixwebsocket/IXWebSocketTransport.cpp index 5095077c..9f02fa90 100644 --- a/ixwebsocket/IXWebSocketTransport.cpp +++ b/ixwebsocket/IXWebSocketTransport.cpp @@ -19,9 +19,9 @@ # endif #endif -#include -#include +// #include #include +#include #include #include @@ -273,12 +273,12 @@ namespace ix { { int N = (int) _rxbuf.size(); - ssize_t ret; + int ret; _rxbuf.resize(N + 1500); ret = _socket->recv((char*)&_rxbuf[0] + N, 1500); - if (ret < 0 && (errno == EWOULDBLOCK || - errno == EAGAIN)) { + if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK || + _socket->getErrno() == EAGAIN)) { _rxbuf.resize(N); break; } @@ -575,8 +575,8 @@ namespace ix { { int ret = _socket->send((char*)&_txbuf[0], _txbuf.size()); - if (ret < 0 && (errno == EWOULDBLOCK || - errno == EAGAIN)) + if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK || + _socket->getErrno() == EAGAIN)) { break; }