Windows support (no TLS yet)

This commit is contained in:
Benjamin Sergeant 2018-10-08 21:42:45 -07:00
parent af12089e7a
commit 71b40c6d6c
5 changed files with 116 additions and 45 deletions

View File

@ -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` 2. Compile the example C++ code. `sh build.sh`
3. Install node.js from [here](https://nodejs.org/en/download/). 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. 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` 5. Bring up a second terminal. `./cmd_websocket_chat bob`
6. Bring up a third terminal. `env USER=bill ./cmd_websocket_chat` 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. 7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end.
## C++ code organization ## C++ code organization

View File

@ -13,6 +13,7 @@
#include <sstream> #include <sstream>
#include <queue> #include <queue>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h>
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
@ -158,13 +159,10 @@ namespace
_webSocket.send(encodeMessage(text)); _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; std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
WebSocketChat webSocketChat(user);
webSocketChat.start(); webSocketChat.start();
while (true) 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; return 0;
} }

View File

@ -6,22 +6,32 @@
#include "IXSocket.h" #include "IXSocket.h"
#include <netdb.h> #ifdef _WIN32
#include <netinet/tcp.h> # include <basetsd.h>
# include <WinSock2.h>
# include <ws2def.h>
# include <WS2tcpip.h>
# include <io.h>
#else
# include <unistd.h>
# include <errno.h>
# include <netdb.h>
# include <netinet/tcp.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <sys/select.h>
# include <sys/stat.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h>
#include <algorithm>
// //
// Linux/Android has a special type of virtual files. select(2) will react // Linux/Android has a special type of virtual files. select(2) will react
@ -35,7 +45,7 @@
// cf Android/Kernel table here // cf Android/Kernel table here
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel // https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
// //
#ifndef __APPLE__ #ifdef __linux__
# include <sys/eventfd.h> # include <sys/eventfd.h>
#endif #endif
@ -51,7 +61,7 @@ namespace ix
_sockfd(-1), _sockfd(-1),
_eventfd(-1) _eventfd(-1)
{ {
#ifndef __APPLE__ #ifdef __linux__
_eventfd = eventfd(0, 0); _eventfd = eventfd(0, 0);
assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform"); assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform");
#endif #endif
@ -61,12 +71,12 @@ namespace ix
{ {
close(); close();
#ifndef __APPLE__ #ifdef __linux__
::close(_eventfd); ::close(_eventfd);
#endif #endif
} }
bool connectToAddress(const struct addrinfo *address, bool Socket::connectToAddress(const struct addrinfo *address,
int& sockfd, int& sockfd,
std::string& errMsg) std::string& errMsg)
{ {
@ -84,7 +94,7 @@ namespace ix
int maxRetries = 3; int maxRetries = 3;
for (int i = 0; i < maxRetries; ++i) 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; sockfd = fd;
return true; return true;
@ -94,7 +104,7 @@ namespace ix
if (errno != EINTR) break; if (errno != EINTR) break;
} }
::close(fd); closeSocket(fd);
sockfd = -1; sockfd = -1;
errMsg = strerror(errno); errMsg = strerror(errno);
return false; return false;
@ -142,7 +152,13 @@ namespace ix
{ {
int flag = 1; int flag = 1;
setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm 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 fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
#endif
#ifdef SO_NOSIGPIPE #ifdef SO_NOSIGPIPE
int value = 1; int value = 1;
@ -163,12 +179,12 @@ namespace ix
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(_sockfd, &rfds); FD_SET(_sockfd, &rfds);
#ifndef __APPLE__ #ifdef __linux__
FD_SET(_eventfd, &rfds); FD_SET(_eventfd, &rfds);
#endif #endif
int sockfd = _sockfd; int sockfd = _sockfd;
int nfds = std::max(sockfd, _eventfd); int nfds = (std::max)(sockfd, _eventfd);
select(nfds + 1, &rfds, nullptr, nullptr, nullptr); select(nfds + 1, &rfds, nullptr, nullptr, nullptr);
onPollCallback(); onPollCallback();
@ -191,7 +207,7 @@ namespace ix
{ {
#ifdef __APPLE__ #ifdef __APPLE__
wakeUpFromPollApple(); wakeUpFromPollApple();
#else #elif defined(__linux__)
wakeUpFromPollLinux(); wakeUpFromPollLinux();
#endif #endif
} }
@ -202,7 +218,7 @@ namespace ix
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
#ifndef __APPLE__ #ifdef __linux__
if (_eventfd == -1) if (_eventfd == -1)
{ {
return false; // impossible to use this socket if eventfd is broken return false; // impossible to use this socket if eventfd is broken
@ -219,7 +235,7 @@ namespace ix
if (_sockfd == -1) return; if (_sockfd == -1) return;
::close(_sockfd); closeSocket(_sockfd);
_sockfd = -1; _sockfd = -1;
} }
@ -245,7 +261,44 @@ namespace ix
flags = MSG_NOSIGNAL; flags = MSG_NOSIGNAL;
#endif #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
}
} }

View File

@ -11,6 +11,8 @@
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
struct addrinfo;
namespace ix namespace ix
{ {
class Socket { class Socket {
@ -20,7 +22,7 @@ namespace ix
Socket(); Socket();
virtual ~Socket(); virtual ~Socket();
static int hostname_connect(const std::string& hostname, int hostname_connect(const std::string& hostname,
int port, int port,
std::string& errMsg); std::string& errMsg);
void configure(); void configure();
@ -38,13 +40,24 @@ namespace ix
virtual int send(const std::string& buffer); virtual int send(const std::string& buffer);
virtual int recv(void* buffer, size_t length); 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: protected:
void wakeUpFromPollApple(); void wakeUpFromPollApple();
void wakeUpFromPollLinux(); void wakeUpFromPollLinux();
void closeSocket(int fd);
std::atomic<int> _sockfd; std::atomic<int> _sockfd;
int _eventfd; int _eventfd;
std::mutex _socketMutex; std::mutex _socketMutex;
private:
bool connectToAddress(const struct addrinfo *address,
int& sockfd,
std::string& errMsg);
}; };
} }

View File

@ -19,9 +19,9 @@
# endif # endif
#endif #endif
#include <unistd.h> // #include <unistd.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <cstdlib> #include <cstdlib>
#include <vector> #include <vector>
@ -273,12 +273,12 @@ namespace ix {
{ {
int N = (int) _rxbuf.size(); int N = (int) _rxbuf.size();
ssize_t ret; int ret;
_rxbuf.resize(N + 1500); _rxbuf.resize(N + 1500);
ret = _socket->recv((char*)&_rxbuf[0] + N, 1500); ret = _socket->recv((char*)&_rxbuf[0] + N, 1500);
if (ret < 0 && (errno == EWOULDBLOCK || if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
errno == EAGAIN)) { _socket->getErrno() == EAGAIN)) {
_rxbuf.resize(N); _rxbuf.resize(N);
break; break;
} }
@ -575,8 +575,8 @@ namespace ix {
{ {
int ret = _socket->send((char*)&_txbuf[0], _txbuf.size()); int ret = _socket->send((char*)&_txbuf[0], _txbuf.size());
if (ret < 0 && (errno == EWOULDBLOCK || if (ret < 0 && (_socket->getErrno() == EWOULDBLOCK ||
errno == EAGAIN)) _socket->getErrno() == EAGAIN))
{ {
break; break;
} }