Проблема подключения к серверу и клиентскому сокету. send (), accept () и многопоточность - PullRequest
2 голосов
/ 28 октября 2011

Я разрабатываю серверную программу на C ++ для получения нескольких клиентских подключений и передачи их в потоки, однако я зашел в тупик.

Все соединения с сокетами работают нормально, как и многопоточность- почти.Пожалуйста, посмотрите мой код ниже (он компилируется и работает нормально).

Я попытался свести его к основам, чтобы вы могли легко следить за ним и занимать меньше всего времени.Я прокомментировал код, чтобы помочь вам увидеть, где проблема, а затем опишу проблему подробно внизу.Если вы можете мне помочь, я был бы очень признателен!

#include <vector>
#include <boost/thread.hpp>
#include "unix_serverSocket.h"
#include "server.h"

extern const string socketAddress;


void do_stuff(ServerSocket *client)
{
    string in;
    string out;

    try
    {
        /* Gets input until the client closes the connection, then throws an exception, breaking out of the loop */
        while (true)
        {
            *client >> in;   /* Receives data from client socket connection */

            /* Assume the input is processed fine and returns the result into 'out' */

            sleep(3);   /* I've put sleep() here to test it's multithreading properly - it isn't */

            *client << out;   /* Returns result to client - send() is called here */

            /* If I put sleep() here instead it multithreads fine, so the server is waiting for send() before it accepts a new client */
        }
    }
    catch (SocketException &)
    {
        delete client;
        return;
    }
}


int main()
{
    try
    {
        ServerSocket server(socketAddress);

        while (true)
        {
            ServerSocket *client = new ServerSocket();

            /* See below */
            server.accept(*client);

            boost::thread newThread(do_stuff, client);
        }
    }
    catch (SocketException &e)
    {
        cout << "Error: " << e.description() << endl;
    }    

    return 0;
}

После того, как клиентское сокетное соединение было передано потоку, main () возвращается к строке:

server.accept(*client);

но затем ждет, пока предыдущее соединение отправит свой результат обратно клиенту через send (), прежде чем оно примет новое соединение - т.е. сервер ожидает, что что-то произойдет в потоке, прежде чем он примет нового клиента!Я не хочу, чтобы это делалось - я хочу, чтобы он отправлял клиентское соединение потоку, а затем сразу принимал больше клиентских соединений и передавал их в другие потоки!

Если вам интересно, почему я создал указатель на сокет здесь ...

ServerSocket *client = new ServerSocket();

... если я не создаю указатель, тогда функция recv ()вызываемый потоком не может получить данные от клиента, что, по-видимому, связано с тем, что поток поверхностно копирует соединение с сокетом клиента, а сборщик мусора не понимает потоки и думает, что клиентское соединение больше не будет использоваться после того, как оно прошлов поток и, таким образом, уничтожая его перед вызовом recv () в потоке.Следовательно, используя указатель, созданный в куче, которая работала.В любом случае, когда я переработал код, используя fork () вместо потоков (что означало, что мне не нужно было создавать сокет в куче), у меня все еще была та же проблема с сервером, который не мог принимать новых клиентов.

Полагаю, мне нужно как-то изменить настройки сервера, чтобы клиент не ожидал отправки () перед тем, как принять новый, однако, несмотря на большое количество Google, я все еще в растерянности!

Вот соответствующий код подключения сокетов на случай, если это поможет (сервер и клиенты находятся в одном блоке и, следовательно, подключаются через локальные сокеты UNIX):

class Socket
{
private:
    int sockfd;
    struct sockaddr_un local;

public:
    Socket();
    virtual ~Socket();

    bool create();
    bool bind(const string &);
    bool listen() const;
    bool accept(Socket &) const;

    bool send(const string &) const;
    int recv(string &) const;

    void close();

    bool is_valid() const 
    { 
        return sockfd != -1;
    }
};


bool Socket::create()
{
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

    if (!is_valid())
    {
        return false;
    }

    int reuseAddress = 1;

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuseAddress, sizeof(reuseAddress)) == -1)
    {
        return false;
    }

    return true;
}


bool Socket::bind(const string &socketAddress)
{
    if (!is_valid())
    {
        return false;
    }

    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, socketAddress.c_str());
    unlink(local.sun_path);
    int len = strlen(local.sun_path) + sizeof(local.sun_family);

    int bind_return = ::bind(sockfd, (struct sockaddr *) &local, len);

    if (bind_return == -1)
    {
        return false;
    }

    return true;
}


bool Socket::listen() const
{
    if (!is_valid())
    {
        return false;
    }

    int listen_return = ::listen(sockfd, MAXCLIENTCONNECTIONS);

    if (listen_return == -1)
    {
        return false;
    }

    return true;
}


bool Socket::accept(Socket &socket) const
{
    int addr_length = sizeof(local);

    socket.sockfd = ::accept(sockfd, (sockaddr *) &local, (socklen_t *) &addr_length);

    if (socket.sockfd <= 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}


int Socket::recv(string &str) const
{
    char buf[MAXRECV + 1];

    str = "";

    memset(buf, 0, MAXRECV + 1);

    int status = ::recv(sockfd, buf, MAXRECV, 0);

    if (status == -1)
    {
        cout << "status == -1   errno == " << errno << "  in Socket::recv" << endl;
        return 0;
    }
    else if (status == 0)
    {
        return 0;
    }
    else
    {
        str = buf;
        return status;
    }
}


bool Socket::send(const string &str) const
{
    int status = ::send(sockfd, str.c_str(), str.size(), MSG_NOSIGNAL);

    if (status == -1)
    {
        return false;
    }
    else
    {
        return true;
    }
}


class ServerSocket : private Socket
{
public:
    ServerSocket(const string &);
    ServerSocket() {};
    virtual ~ServerSocket();

    void accept(ServerSocket &);

    const ServerSocket & operator << (const string &) const;
    const ServerSocket & operator >> (string &) const;
};


ServerSocket::ServerSocket(const string &socketAddress)
{   
    if (!Socket::create())
    {
        throw SocketException("Could not create server socket");
    }

    if (!Socket::bind(socketAddress))
    {
        throw SocketException("Could not bind to port");
    }

    if (!Socket::listen())
    {
        throw SocketException("Could not listen to socket");
    }
}


void ServerSocket::accept(ServerSocket &socket)
{   
    if (!Socket::accept(socket))
    {
        throw SocketException("Could not accept socket");
    }
}


const ServerSocket & ServerSocket::operator << (const string &str) const
{   
    if (!Socket::send(str))
    {
        throw SocketException("Could not write to socket");
    }

    return *this;
}


const ServerSocket & ServerSocket::operator >> (string &str) const
{
    if (!Socket::recv(str))
    {
        throw SocketException("Could not read from socket");
    }

    return *this;
}

Ответы [ 2 ]

1 голос
/ 14 ноября 2011

Я понял это! Причина, по которой клиенты не использовали многопоточность, заключалась в том, что программа, создающая клиентские соединения, делала это в мьютексе - следовательно, она не будет создавать новое соединение, пока старое не получит ответ от сервера, и, следовательно, сервер быть только однопоточным! Итак, короче говоря, моя серверная программа была в порядке, и это была проблема на стороне клиента - извините за трату вашего времени - я даже не рассматривал возможность, пока полностью не переработал структуру программы, вместо этого разместив многопоточность на стороне клиента, который затем выявил проблему.

Спасибо за вашу помощь!

0 голосов
/ 10 ноября 2011

Ваши розетки блокируют !Это означает, что они будут ждать завершения операции перед возвратом.

Вот как вы делаете сокет неблокирующим:

bool nonblock(int sock)
{
    int flags;

    flags = fcntl(sock, F_GETFL, 0);
    flags |= O_NONBLOCK;

    return (fcntl(sock, F_SETFL, flags) == 0);
}

Теперь функции accept, readи write все вернет ошибку, если сокет заблокируется, установив для переменной errno значение EWOULDBLOCK или, возможно, EAGAIN.

Если вы хотите дождаться готовности сокета к чтениюили написание, вы можете использовать функцию select.Для прослушивающих сокетов (тот, который вы делаете accept на) он будет готов к чтению, когда будет принято новое соединение.

...