6
\$\begingroup\$

I'm trying to use sockets to create a simple client and server architecture and I'm wondering if there is any other way to create non-blocking, 'async' sockets instead using fcntl(), select() or Boost Library? How can I implement this in a C++ style? I'm using threads to accept new connections and receive data, but in this case it doesn't work well enough (receive hangs, waiting for data, vector isn't a good container for thread based operations etc.).

My code:

const char * SERVER_HOST = "127.0.0.1";
const unsigned short MAX_CONNECTIONS = 10;
const int MAX_DATA_BUFFER = 4096;
const unsigned int SERVER_PORT = 8989;

std::vector<int> clients;

int acceptClient(int serverFd, sockaddr* saddr, socklen_t *sin_size)
{
    while(true)
    {
        int newSocketFd = accept(serverFd, saddr, sin_size);

        if (newSocketFd != -1)
        {
            clients.push_back(newSocketFd);
            std::cout << "New client!" << std::endl;
        }
    }
}

int recive()
{
    while(true)
    {
        for(auto x : clients)
        {
            char buffer[4096];
            int nbytes = recv(x, buffer, sizeof(buffer), 0);
            std::cout << "Socket descriptor: " << x << std::endl;
            if (nbytes != -1)
            {
                buffer[nbytes] = '\0';
                string str(buffer);
                std::cout << "Recived from: " << x << " " << str << std::endl;
            }

            if (nbytes == 0)
            {
                std::cout << "Connection refused..." << std::endl;
                std::remove_if(clients.begin(), clients.end(), [x](int fd)->bool{fd = x;});
            }
        }
    }
}

int main()
{
    int serverFd;
    struct sockaddr_in serverSockAddr = {};
    serverSockAddr.sin_addr.s_addr = INADDR_ANY;
    serverSockAddr.sin_port = htons(SERVER_PORT);
    serverSockAddr.sin_family = AF_INET;
    memset(&serverSockAddr.sin_zero, '\0', 8);

    if ((serverFd= socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    std::cout << "Socket created..." << std::endl;

    if (bind(serverFd, reinterpret_cast<sockaddr * >(&serverSockAddr), sizeof(sockaddr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    std::cout << "Bind complete..." << std::endl;

    int yes = 1;
    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    {
        perror("setsockpt");
        exit(EXIT_FAILURE);
    }
    std::cout << "Socket cleared if stuck..." << std::endl;

    if (listen(serverFd, MAX_CONNECTIONS) == -1)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    std::cout << "Socket listen..." << std::endl;

    int clientFd;
    socklen_t sin_size = sizeof(struct sockaddr_in);
    sockaddr_in clientSockAddr = {};

    std::thread t1 (acceptClient, serverFd, reinterpret_cast<struct sockaddr*>(&clientSockAddr), &sin_size);
    std::thread t2 (recive);

    char buf[1024];
    std::cin>>buf;

    return 0;
\$\endgroup\$

closed as off-topic by Daniel, Stephen Rauch, Billal Begueradj, t3chb0t, Dannnno Jun 4 '18 at 13:50

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Code not implemented or not working as intended: Code Review is a community where programmers peer-review your working code to address issues such as security, maintainability, performance, and scalability. We require that the code be working correctly, to the best of the author's knowledge, before proceeding with a review." – Daniel, Stephen Rauch, Billal Begueradj, t3chb0t, Dannnno
If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ I would use libEvent that will tell you when a socket has data and you can go get it. But that is very low level. Above that there is boost async io boost.asio library that virtualize sockets into sinks and streams. There is nothing really in between (that I know of). \$\endgroup\$ – Martin York May 15 '18 at 22:23
  • \$\begingroup\$ PS. Using threads to increase parallelism to read more from sockets is the wrong way to go. Socket communication is inherently slow (compared to processors) and threads are expensive and bulky. The better way to go is have a single thread multiplex across thousands of connections. This is how modern web servers work. See C10K. \$\endgroup\$ – Martin York May 15 '18 at 22:28
  • \$\begingroup\$ If you are interested I have wrapped sockets. To provide a non blocking std::istream. So you can create a socket connection and it behaves like a normal std::istream. Any blocking operation causes the thread to finds another stream to handle. See ThorsNissa. It is noware as polished or advanced as boost asio but I work on it when I have time. Current documentation here: lokiastari.com/ThorsNisse/#introduction \$\endgroup\$ – Martin York May 15 '18 at 22:37
  • \$\begingroup\$ 'it doesn't work well enough' suggests you are asking about improving broken / unfinished code. If so, this question is off-topic here. \$\endgroup\$ – Daniel Jun 3 '18 at 21:44
  • \$\begingroup\$ @MartinYork Meta discussion here. \$\endgroup\$ – Daniel Jun 4 '18 at 15:52