Сервер және клиент розеткасының қосылымы қайтадан шығарылады. send (), accept () және multi-threading

Мен C ++-де серверлік бағдарламаны бірнеше клиенттік байланыстарды алу және оларды ағындарға көшіру үшін жасаймын, бірақ мен қиындыққа жеттім.

Розетка қосылымдары барлығы жақсы жұмыс істейді, ал мультижолдау - дерлік. Төмендегі кодты көріңіз (ол компиляция жасайды және жақсы жұмыс істейді).

Мен оны оңай ұстап, өзіңіздің уақытыңызды азайтуға мүмкіндік беру үшін оны төмендетуге тырыстым. Кодты түсіндірдім, мәселенің қай жерде екендігін көруге көмектесу үшін, содан кейін мәселені төменгі бөлігінде егжей-тегжейлі сипаттаймын. Егер сіз маған көмектесе алсаңыз, онда мен өте ризамын!

#include 
#include 
#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);

бірақ содан кейін алдыңғы нәтижені күтіп, оның нәтижесін кері қайтарады                 клиент жаңа жіберуді қабылдамас бұрын (яғни, жіберуші арқылы) (яғни сервер күтуде)                 жаңа клиентті қабылдамас бұрын жіпте қандай да бір нәрсе болуы мүмкін! Мен емеспін                 мұны істеуді қалаймын - клиенттің байланысын жіпке жіберіп, содан кейін қабылдағым келеді                 көп клиент байланыстары дереу және оларды одан да көп ағындарға жіберіңіз!

Егер сіз мұнда розеткаға көрсеткішті не үшін жасағанымды ойласаңыз ...

ServerSocket *client = new ServerSocket();

... Егер көрсеткішті жасамасам, онда жіптен шақырылған recv() функциясы клиенттің деректерін қабылдамайды, ол клиенттің ұяшығына қосылымды ұсақ көшіруге байланысты, және қоқыс жинаушы ағындарды және клиент қосылымы жіпке жіберілгеннен кейін пайдаланылмайды деп ойланып, recv() функциясына шақырылғанға дейін жойылады. Демек, ол жұмыс істеген қабаттағы көрсеткішті қолданды. Қалай болғанда да, мен кодты қайта өңдегенде, fork() ағындарының орнына (бұл менің үйімдегі ұяшықтарды жасаудың қажеті жоқ дегенді білдірді), менде жаңа серверді қабылдай алмайтын сервермен бірдей проблема болды.

Менің ойымша сервер параметрлерін қандай да бір түрде өзгерту қажет, себебі ол клиентті жаңа қабылдамас бұрын жіберуді күтпейді, бірақ Google Googling-ге қарамастан, мен бәрібір жоғалмаймын!

Міне, ол көмектесетін жағдайда тиісті сокет қосылым коды (сервер мен клиенттер бірдей қорапта орналасқан және осылайша жергілікті 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
C ++ ортаңызда қоқыс жинағыштың бар екеніне сенімдісіз бе? C ++ бағдарламаларының 99% қоқыс жинағышсыз іске қосуға конфигурацияланған.
қосылды автор Jeremy Friesner, көзі
accept() деп аталатын кодты көрсетіңіз және ағындарды бастаңыз.
қосылды автор Remy Lebeau, көзі
'Сервердегі' қабылдау() қоңырауы біраз 'өшірулі'. Мен пайдаланған тілдерде/libs, accept() ешқандай параметрлерді қабылдамайды және сокет көрсеткіші/instance/whatever қайтарады. Бұл әдеттен тыс (бірақ мүмкін емес болса да), оның орнына сілтеме анықтамалық параметрді қабылдайды. Сіздің кітапханаңыздағы қабылдау() қалай жұмыс істейді? Яғни, жаңа() ұяшығының данасын жасау керек және қабылдауға() өту керек.
қосылды автор Martin James, көзі
Сервердің ұясында bind (2) және listen (2) ) қайда?
қосылды автор Nikolai Fetissov, көзі
Кешіріңіз, егер сіз оны бірнеше рет алған болсаңыз! Пожалуйста, жоғарыдан қараңыз - Мен өзімнің түпнұсқалық жазбамды кейбір ұяшық кодын қосу үшін редакцияладым. Жегілуді бастауға келетін болсақ, бұл жай ғана конструктордағы бірінші аргументтің функциясындағы ағыны бастайтын Boost кітапханасында «boost» :: thread конструкторы, кез-келген қосымша аргументтермен сол функцияға дәлел ретінде берілсе - менің тақырып файлдарымда жасырылған менің қосымша код жоқ болса, ол ішінде болады. Сондай-ақ, бұл UNIX ұяшығының қосылымы екенін айтқан жөн. Рахмет
қосылды автор Philip, көзі
Ия, розетка байланысы жойылғанын тексердім, ал олар мені үймедегі байланыстарды құруға әкелді.
қосылды автор Philip, көзі
Кешірім - мен оларды қиналмадым. Мен оларды қазір қостым.
қосылды автор Philip, көзі

2 жауаптар

Мен оны түсіндім! Клиенттердің мультимедиа болмағандықтан, клиент байланыстарын жасайтын бағдарлама mutex-да болғандықтан, ол ескі серверден жауап алғанға дейін жаңа байланыс жасамайды, осылайша сервер серверге пайда болды жалғыз жіптер болуы керек! Қысқаша айтқанда, менің жоғарыда көрсетілген серверлік бағдарлама жақсы болды және бұл сіздің клиенттің соңында - сіздің уақытыңызды ысырап ету үшін кешірім сұрады - тіпті бағдарлама құрылымын толығымен клиенттің соңында орналастыру арқылы мүмкіндігін қарастырмадым, содан кейін бұл мәселені анықтады.

Барлық көмегіңізге рахмет!

1
қосылды

Сіздің розеткаларыңыз бұғатталған ! Бұл оралудан бұрын операцияны аяқтауды күтеді.

Осылайша сіз розеткадан блокталмайсыз:

bool nonblock(int sock)
{
    int flags;

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

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

errno параметрін орнату кезінде accept , read және write айнымалы EWOULDBLOCK немесе мүмкін EAGAIN .

Егер сокеттің оқуға немесе жазуға дайын болуын күтсеңіз, таңдау функциясын қолдануға болады. Розеткаларды тыңдау үшін ( accept ) сіз жаңа қосылым қабылданған кезде оқуға дайын боласыз.

0
қосылды
Жауап бергеніңіз үшін рахмет - розеткаларды бұғаттан босатып, select() функциясын қолданып болғаннан кейін, мен не болғанын білмедім, өйткені бұл мультипликация емес, мультиплекс еді. Дегенмен, мен шешімді таптым - төменде қараңыз!
қосылды автор Philip, көзі