/* * IXWebSocketServer.cpp * Author: Benjamin Sergeant * Copyright (c) 2018 Machine Zone, Inc. All rights reserved. */ #include "IXWebSocketServer.h" #include "IXWebSocketTransport.h" #include "IXWebSocket.h" #include #include #include #include #include #include namespace ix { WebSocketServer::WebSocketServer(int port, int backlog) : _port(port), _backlog(backlog) { } WebSocketServer::~WebSocketServer() { } void WebSocketServer::setOnConnectionCallback(const OnConnectionCallback& callback) { _onConnectionCallback = callback; } std::pair WebSocketServer::listen() { struct sockaddr_in server; /* server address information */ /* * Get a socket for accepting connections. */ if ((_serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { std::stringstream ss; ss << "WebSocketServer::listen() error creating socket): " << strerror(errno); return std::make_pair(false, ss.str()); } int enable = 1; if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { std::stringstream ss; ss << "WebSocketServer::listen() error calling setsockopt(SO_REUSEADDR): " << strerror(errno); return std::make_pair(false, ss.str()); } /* * Bind the socket to the server address. */ server.sin_family = AF_INET; server.sin_port = htons(_port); // Using INADDR_ANY trigger a pop-up box as binding to any address is detected // by the osx firewall. We need to codesign the binary with a self-signed cert // to allow that, but this is a bit of a pain. (this is what node or python would do). // // Using INADDR_LOOPBACK also does not work ... while it should. // server.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(_serverFd, (struct sockaddr *)&server, sizeof(server)) < 0) { std::stringstream ss; ss << "WebSocketServer::listen() error calling bind: " << strerror(errno); return std::make_pair(false, ss.str()); } /* * Listen for connections. Specify the tcp backlog. */ if (::listen(_serverFd, _backlog) != 0) { std::stringstream ss; ss << "WebSocketServer::listen() error calling listen: " << strerror(errno); return std::make_pair(false, ss.str()); } return std::make_pair(true, ""); } void WebSocketServer::run() { std::future f; for (;;) { // Accept a connection. struct sockaddr_in client; // client address information int clientFd; // socket connected to client socklen_t addressLen = sizeof(socklen_t); if ((clientFd = accept(_serverFd, (struct sockaddr *)&client, &addressLen)) == -1) { // FIXME: that error should be propagated std::cerr << "WebSocketServer::run() error accepting connection: " << strerror(errno) << std::endl; continue; } // Launch the handleConnection work asynchronously in its own thread. // // the destructor of a future returned by std::async blocks, // so we need to declare it outside of this loop f = std::async(std::launch::async, &WebSocketServer::handleConnection, this, clientFd); } } // // FIXME: make sure we never run into reconnectPerpetuallyIfDisconnected // void WebSocketServer::handleConnection(int fd) { std::shared_ptr webSocket(new WebSocket); _onConnectionCallback(webSocket); _clients.insert(webSocket); webSocket->start(); auto status = webSocket->connectToSocket(fd); if (!status.success) { std::cerr << "WebSocketServer::handleConnection() error: " << status.errorStr << std::endl; return; } // We can probably do better than this busy loop, with a condition variable. while (webSocket->isConnected()) { std::chrono::duration wait(10); std::this_thread::sleep_for(wait); } _clients.erase(webSocket); std::cerr << "WebSocketServer::handleConnection() done" << std::endl; } }