BSD Sockets - Как использовать неблокирующие розетки? - PullRequest
3 голосов
/ 15 июля 2011

Я пытаюсь использовать неблокирующие сокеты TCP.Проблема в том, что они все еще блокируют.Код ниже -

код сервера -

struct sockaddr name;
char buf[80];

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;   //sock is this socket, new_sd is connection socket

    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    //make socket
    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nBind error %m", errno);
        exit(1);
    }

    //unlink and bind
    unlink("127.0.0.1");
    if(bind (sock, &name, adrlen) < 0)
        printf("\nBind error %m", errno);

    //listen
    if(listen(sock, 5) < 0)
        printf("\nListen error %m", errno);

    //accept
    new_sd = accept(sock, &name, (socklen_t*)&adrlen);
    if( new_sd < 0) {
        cout<<"\nserver accept failure "<<errno;
        exit(1);
    }

    //set nonblock
    set_nonblock(new_sd);

    char* in = new char[80];
    std::string out = "Got it";
    int numSent;
    int numRead;

    while( !(in[0] == 'q' && in[1] == 'u' && in[2] == 'i' && in[3] == 't') ) {

        //clear in buffer
        for(int i=0;i<80;i++)
            in[i] = ' ';

        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str()) > 0) {
            numSent = send(new_sd, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

        numRead = recv(new_sd, in, 80, 0);
        if(numRead > 0)
            cout<<"\nData read from client - "<<in;

     }   //end while

     cout<<"\nExiting normally\n";
     return 0;
}

код клиента -

struct sockaddr name;

void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}

int main(int agrc, char** argv) {

    int sock, new_sd, adrlen;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    if (sock < 0) {
        printf("\nserver socket failure %m", errno);
        exit(1);
    }

    //stuff for server socket
    name.sa_family = AF_UNIX;
    strcpy(name.sa_data, "127.0.0.1");
    adrlen = strlen(name.sa_data) + sizeof(name.sa_family);

    if(connect(sock, &name, adrlen) < 0) {
        printf("\nclient connection failure %m", errno);
        exit(1);
    }

    cout<<"\nSuccessful connection\n";

    //set nonblock
    set_nonblock(sock);

    std::string out;
    char* in = new char[80];
    int numRead;
    int numSent;


    while(out.compare("quit")) {

        //clear in
        for(int i=0;i<80;i++)
            in[i] = '\0';


        numRead = recv(sock, in, 80, 0);

        if(numRead > 0)
            cout<<"\nData read from server - "<<in;


        cout<<"\n";
        out.clear();
        cin>>out;
        cin.get();

        //if we typed something, send it
        if(strlen(out.c_str())) {
            numSent = send(sock, out.c_str(), strlen(out.c_str()), 0);
            cout<<"\n"<<numSent<<" bytes sent";
        }

    }   //end while


    cout<<"\nExiting normally\n";
    return 0;
}

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

Кроме того, я использовал файл вместо своего адреса 127.0.0.1 в качестве данных sockaddr.Если это не то, как его следует использовать должным образом, не стесняйтесь говорить об этом (он работал так же, как и раньше с файлом, поэтому я просто сохранил его так).

Любая помощь приветствуется.

Ответы [ 4 ]

4 голосов
/ 15 июля 2011

Общий подход для TCP-сервера, где вы хотите обрабатывать много соединений одновременно:

  • сделать неблокирующим сокет прослушивания
  • добавить его к select(2) или poll(2) чтение набор событий
  • ввод select(2) / poll(2) цикл
  • при проверке пробуждения, если этопрослушивающий сокет, затем
    • accept(2)
    • проверка на сбой (клиент, возможно, уже прервал попытку подключения)
    • сделать заново созданнымнеблокирование клиентского сокета, добавьте его в набор событий опроса
  • иначе, если это один из клиентских сокетов
    • потребляют ввод, обрабатывают его
    • следите за EAGAIN кодом ошибки - на самом деле это не ошибка, а указание на отсутствие ввода сейчас
    • если читать ноль байтов - клиент закрыл соединение,close(2) клиентский сокет, удалите его из набора событий
  • набор событий повторной инициализации (пропускаем это обычная ошибка при select(2))
  • повторить цикл

Клиентская сторона немного проще, поскольку у вас есть только один сокет.Расширенные приложения, такие как веб-браузеры, которые обрабатывают много соединений, часто делают неблокирование connect(2), хотя.

3 голосов
/ 15 июля 2011

Всякий раз, когда я запускаю его, сервер все еще ждет, пока я что-то отправлю, прежде чем он прочитает и выведет то, что отправил клиент.

Ну, вот как вы это написали.Вы блокируете ввод-вывод из stdin, а затем и только тогда отправляете / получаете.

cin>>out;
cin.get();

Кроме того, вы используете локальный сокет (AF_UNIX), который создает специальный файл в вашей файловой системе для межпроцессного взаимодействия -это другой механизм, чем IP, и определенно не TCP, как вы указали в своем вопросе.Я полагаю, вы могли бы назвать файл 127.0.0.1 , но это на самом деле не имеет смысла и подразумевает путаницу с вашей стороны, потому что это IP-адрес обратной связи.Вы можете использовать AF_INET для IP.

Для отличного руководства по работе с сетями Unix я бы порекомендовал http://beej.us/guide/bgnet/

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

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

man select_tut

для получения дополнительной информации.

3 голосов
/ 15 июля 2011

Я думаю, что вам нужно установить неблокирование раньше (т.е. получить сокет, а затем установить его без блокировки)

также проверить, что fcntl для его установки действительно работал

0 голосов
/ 15 июля 2011

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

http://beej.us/guide/bgnet/output/html/multipage/advanced.html

Я бы порекомендовал прочитать, что бей говорит о select.Это выглядит немного пугающе, но действительно полезно и просто в использовании, если вам нужно немного времени, чтобы обернуть голову вокруг него.

...