First import
This commit is contained in:
		
							
								
								
									
										251
									
								
								ixwebsocket/IXSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								ixwebsocket/IXSocket.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,251 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  IXSocket.cpp
 | 
			
		||||
 *  Author: Benjamin Sergeant
 | 
			
		||||
 *  Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXSocket.h"
 | 
			
		||||
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.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>
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// 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
 | 
			
		||||
//
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
# include <sys/eventfd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Android needs extra headers for TCP_NODELAY and IPPROTO_TCP
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
# include <linux/in.h>
 | 
			
		||||
# include <linux/tcp.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace ix 
 | 
			
		||||
{
 | 
			
		||||
    Socket::Socket() : 
 | 
			
		||||
        _sockfd(-1),
 | 
			
		||||
        _eventfd(-1)
 | 
			
		||||
    {
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
        _eventfd = eventfd(0, 0);
 | 
			
		||||
        assert(_eventfd != -1 && "Panic - eventfd not functioning on this platform");
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Socket::~Socket()
 | 
			
		||||
    {
 | 
			
		||||
        close();
 | 
			
		||||
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
        ::close(_eventfd);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool connectToAddress(const struct addrinfo *address, 
 | 
			
		||||
                          int& sockfd,
 | 
			
		||||
                          std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        sockfd = -1;
 | 
			
		||||
 | 
			
		||||
        int fd = socket(address->ai_family,
 | 
			
		||||
                        address->ai_socktype,
 | 
			
		||||
                        address->ai_protocol);
 | 
			
		||||
        if (fd < 0)
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = "Cannot create a socket";
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int maxRetries = 3;
 | 
			
		||||
        for (int i = 0; i < maxRetries; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (connect(fd, address->ai_addr, address->ai_addrlen) != -1)
 | 
			
		||||
            {
 | 
			
		||||
                sockfd = fd;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // EINTR means we've been interrupted, in which case we try again.
 | 
			
		||||
            if (errno != EINTR) break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ::close(fd);
 | 
			
		||||
        sockfd = -1;
 | 
			
		||||
        errMsg = strerror(errno);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Socket::hostname_connect(const std::string& hostname,
 | 
			
		||||
                                 int port,
 | 
			
		||||
                                 std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        struct addrinfo hints;
 | 
			
		||||
        memset(&hints, 0, sizeof(hints));
 | 
			
		||||
        hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
 | 
			
		||||
        hints.ai_family = AF_UNSPEC;
 | 
			
		||||
        hints.ai_socktype = SOCK_STREAM;
 | 
			
		||||
 | 
			
		||||
        std::string sport = std::to_string(port);
 | 
			
		||||
 | 
			
		||||
        struct addrinfo *res = nullptr;
 | 
			
		||||
        int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), 
 | 
			
		||||
                                             &hints, &res);
 | 
			
		||||
        if (getaddrinfo_result)
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = gai_strerror(getaddrinfo_result);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int sockfd = -1;
 | 
			
		||||
 | 
			
		||||
        // iterate through the records to find a working peer
 | 
			
		||||
        struct addrinfo *address;
 | 
			
		||||
        bool success = false;
 | 
			
		||||
        for (address = res; address != nullptr; address = address->ai_next)
 | 
			
		||||
        {
 | 
			
		||||
            success = connectToAddress(address, sockfd, errMsg);
 | 
			
		||||
            if (success)
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        freeaddrinfo(res);
 | 
			
		||||
        return sockfd;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Socket::configure()
 | 
			
		||||
    {
 | 
			
		||||
        int flag = 1;
 | 
			
		||||
        setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag)); // Disable Nagle's algorithm
 | 
			
		||||
        fcntl(_sockfd, F_SETFL, O_NONBLOCK); // make socket non blocking
 | 
			
		||||
 | 
			
		||||
#ifdef SO_NOSIGPIPE
 | 
			
		||||
        int value = 1;
 | 
			
		||||
        setsockopt(_sockfd, SOL_SOCKET, SO_NOSIGPIPE, 
 | 
			
		||||
                   (void *)&value, sizeof(value));
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Socket::poll(const OnPollCallback& onPollCallback)
 | 
			
		||||
    {
 | 
			
		||||
        if (_sockfd == -1)
 | 
			
		||||
        {
 | 
			
		||||
            onPollCallback();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fd_set rfds;
 | 
			
		||||
        FD_ZERO(&rfds);
 | 
			
		||||
        FD_SET(_sockfd, &rfds);
 | 
			
		||||
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
        FD_SET(_eventfd, &rfds);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        int sockfd = _sockfd;
 | 
			
		||||
        int nfds = std::max(sockfd, _eventfd);
 | 
			
		||||
        select(nfds + 1, &rfds, nullptr, nullptr, nullptr);
 | 
			
		||||
 | 
			
		||||
        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()
 | 
			
		||||
    {
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
        wakeUpFromPollApple();
 | 
			
		||||
#else
 | 
			
		||||
        wakeUpFromPollLinux();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Socket::connect(const std::string& host,
 | 
			
		||||
                         int port,
 | 
			
		||||
                         std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
			
		||||
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
        if (_eventfd == -1)
 | 
			
		||||
        {
 | 
			
		||||
            return false; // impossible to use this socket if eventfd is broken
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        _sockfd = Socket::hostname_connect(host, port, errMsg);
 | 
			
		||||
        return _sockfd != -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Socket::close()
 | 
			
		||||
    {
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_socketMutex);
 | 
			
		||||
 | 
			
		||||
        if (_sockfd == -1) return;
 | 
			
		||||
 | 
			
		||||
        ::close(_sockfd);
 | 
			
		||||
        _sockfd = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Socket::send(char* buffer, size_t length)
 | 
			
		||||
    {
 | 
			
		||||
        int flags = 0;
 | 
			
		||||
#ifdef MSG_NOSIGNAL
 | 
			
		||||
        flags = MSG_NOSIGNAL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        return (int) ::send(_sockfd, buffer, length, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Socket::send(const std::string& buffer)
 | 
			
		||||
    {
 | 
			
		||||
        return send((char*)&buffer[0], buffer.size());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Socket::recv(void* buffer, size_t length)
 | 
			
		||||
    {
 | 
			
		||||
        int flags = 0;
 | 
			
		||||
#ifdef MSG_NOSIGNAL
 | 
			
		||||
        flags = MSG_NOSIGNAL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        return (int) ::recv(_sockfd, buffer, length, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user