stopping connection on Linux does not close the socket, which can create problem when re-starting the connection

This commit is contained in:
Benjamin Sergeant 2018-11-01 17:02:49 -07:00
parent 7117c74142
commit c1ed83a005
5 changed files with 26 additions and 59 deletions

View File

@ -12,12 +12,14 @@ set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF) set (CMAKE_CXX_EXTENSIONS OFF)
set( IXWEBSOCKET_SOURCES set( IXWEBSOCKET_SOURCES
ixwebsocket/IXEventFd.cpp
ixwebsocket/IXSocket.cpp ixwebsocket/IXSocket.cpp
ixwebsocket/IXWebSocket.cpp ixwebsocket/IXWebSocket.cpp
ixwebsocket/IXWebSocketTransport.cpp ixwebsocket/IXWebSocketTransport.cpp
) )
set( IXWEBSOCKET_HEADERS set( IXWEBSOCKET_HEADERS
ixwebsocket/IXEventFd.h
ixwebsocket/IXSocket.h ixwebsocket/IXSocket.h
ixwebsocket/IXWebSocket.h ixwebsocket/IXWebSocket.h
ixwebsocket/IXWebSocketTransport.h ixwebsocket/IXWebSocketTransport.h

View File

@ -32,22 +32,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <algorithm> #include <algorithm>
#include <iostream>
//
// Linux/Android has a special type of virtual files. select(2) will react
// when reading/writing to those files, unlike closing sockets.
//
// https://linux.die.net/man/2/eventfd
//
// eventfd was added in Linux kernel 2.x, and our oldest Android (Kitkat 4.4)
// is on Kernel 3.x
//
// cf Android/Kernel table here
// https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
//
#ifdef __linux__
# include <sys/eventfd.h>
#endif
// Android needs extra headers for TCP_NODELAY and IPPROTO_TCP // Android needs extra headers for TCP_NODELAY and IPPROTO_TCP
#ifdef ANDROID #ifdef ANDROID
@ -58,22 +43,14 @@
namespace ix namespace ix
{ {
Socket::Socket() : Socket::Socket() :
_sockfd(-1), _sockfd(-1)
_eventfd(-1)
{ {
#ifdef __linux__
_eventfd = eventfd(0, 0);
assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform");
#endif
} }
Socket::~Socket() Socket::~Socket()
{ {
close(); close();
#ifdef __linux__
::close(_eventfd);
#endif
} }
bool Socket::connectToAddress(const struct addrinfo *address, bool Socket::connectToAddress(const struct addrinfo *address,
@ -180,36 +157,20 @@ namespace ix
FD_SET(_sockfd, &rfds); FD_SET(_sockfd, &rfds);
#ifdef __linux__ #ifdef __linux__
FD_SET(_eventfd, &rfds); FD_SET(_eventfd.getFd(), &rfds);
#endif #endif
int sockfd = _sockfd; int sockfd = _sockfd;
int nfds = (std::max)(sockfd, _eventfd); int nfds = (std::max)(sockfd, _eventfd.getFd());
select(nfds + 1, &rfds, nullptr, nullptr, nullptr); select(nfds + 1, &rfds, nullptr, nullptr, nullptr);
onPollCallback(); onPollCallback();
} }
void Socket::wakeUpFromPollApple()
{
close(); // All OS but Linux will wake up select
// when closing the file descriptor watched by select
}
void Socket::wakeUpFromPollLinux()
{
std::string str("\n"); // this will wake up the thread blocked on select
const void* buf = reinterpret_cast<const void*>(str.c_str());
write(_eventfd, buf, str.size());
}
void Socket::wakeUpFromPoll() void Socket::wakeUpFromPoll()
{ {
#ifdef __APPLE__ // this will wake up the thread blocked on select, only needed on Linux
wakeUpFromPollApple(); _eventfd.notify();
#elif defined(__linux__)
wakeUpFromPollLinux();
#endif
} }
bool Socket::connect(const std::string& host, bool Socket::connect(const std::string& host,
@ -218,12 +179,7 @@ namespace ix
{ {
std::lock_guard<std::mutex> lock(_socketMutex); std::lock_guard<std::mutex> lock(_socketMutex);
#ifdef __linux__ if (!_eventfd.clear()) return false;
if (_eventfd == -1)
{
return false; // impossible to use this socket if eventfd is broken
}
#endif
_sockfd = Socket::hostname_connect(host, port, errMsg); _sockfd = Socket::hostname_connect(host, port, errMsg);
return _sockfd != -1; return _sockfd != -1;

View File

@ -11,6 +11,8 @@
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
#include "IXEventFd.h"
struct addrinfo; struct addrinfo;
namespace ix namespace ix
@ -45,14 +47,11 @@ namespace ix
static void cleanup(); // Required on Windows to cleanup WinSocket static void cleanup(); // Required on Windows to cleanup WinSocket
protected: protected:
void wakeUpFromPollApple();
void wakeUpFromPollLinux();
void closeSocket(int fd); void closeSocket(int fd);
std::atomic<int> _sockfd; std::atomic<int> _sockfd;
int _eventfd;
std::mutex _socketMutex; std::mutex _socketMutex;
EventFd _eventfd;
private: private:
bool connectToAddress(const struct addrinfo *address, bool connectToAddress(const struct addrinfo *address,

View File

@ -150,6 +150,7 @@ namespace ix
SSL_CTX* ctx = SSL_CTX_new(_ssl_method); SSL_CTX* ctx = SSL_CTX_new(_ssl_method);
if (ctx) if (ctx)
{ {
// To skip verification, pass in SSL_VERIFY_NONE
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, openssl_verify_callback); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, openssl_verify_callback);
SSL_CTX_set_verify_depth(ctx, 4); SSL_CTX_set_verify_depth(ctx, 4);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);

View File

@ -34,6 +34,7 @@
#include <regex> #include <regex>
#include <unordered_map> #include <unordered_map>
#include <random> #include <random>
#include <algorithm>
namespace ix { namespace ix {
@ -293,14 +294,18 @@ namespace ix {
// i is end of string (\0), i-colon is length of string minus key; // i is end of string (\0), i-colon is length of string minus key;
// subtract 1 for '\0', 1 for '\n', 1 for '\r', // subtract 1 for '\0', 1 for '\n', 1 for '\r',
// 1 for the ' ' after the ':', and total is -4 // 1 for the ' ' after the ':', and total is -4
headers[lineStr.substr(0, colon)] = std::string name(lineStr.substr(0, colon));
lineStr.substr(colon + 2, i - colon - 4);
// Make the name lower case.
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
headers[name] = lineStr.substr(colon + 2, i - colon - 4);
} }
} }
char output[29] = {}; char output[29] = {};
WebSocketHandshake::generate(secWebSocketKey.c_str(), output); WebSocketHandshake::generate(secWebSocketKey.c_str(), output);
if (std::string(output) != headers["Sec-WebSocket-Accept"]) if (std::string(output) != headers["sec-websocket-accept"])
{ {
std::string errorMsg("Invalid Sec-WebSocket-Accept value"); std::string errorMsg("Invalid Sec-WebSocket-Accept value");
return WebSocketInitResult(false, status, errorMsg); return WebSocketInitResult(false, status, errorMsg);
@ -319,6 +324,9 @@ namespace ix {
void WebSocketTransport::setReadyState(ReadyStateValues readyStateValue) void WebSocketTransport::setReadyState(ReadyStateValues readyStateValue)
{ {
// No state change, return
if (_readyState == readyStateValue) return;
if (readyStateValue == CLOSED) if (readyStateValue == CLOSED)
{ {
std::lock_guard<std::mutex> lock(_closeDataMutex); std::lock_guard<std::mutex> lock(_closeDataMutex);
@ -707,6 +715,7 @@ namespace ix {
sendOnSocket(); sendOnSocket();
_socket->wakeUpFromPoll(); _socket->wakeUpFromPoll();
_socket->close();
} }
} // namespace ix } // namespace ix