Сокеты BSD - как использовать неблокирующие сокеты?

Я пытаюсь использовать неблокирующие сокеты 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. Если это не то, как его следует использовать должным образом, не стесняйтесь сказать об этом (он работал так, как раньше работал с файлом, поэтому я просто оставил его таким).

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


person Sterling    schedule 14.07.2011    source источник


Ответы (4)


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

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

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 (), как правило, намного лучше и гибче (и более переносимо). пытаться

мужчина select_tut

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

person Josh    schedule 14.07.2011
comment
Спасибо, я посмотрю на fork () и select (). Всякий раз, когда я меняю все AF_UNIX на AF_INET, я получаю ошибку недопустимого аргумента, когда клиент пытается подключиться. Должен ли я менять его только на стороне сервера? - person Sterling; 15.07.2011
comment
Что ж, вам нужно будет изменить и сервер, и клиент на AF_INET. Разница между ними велика: один - это локальный (или unix) сокет, другой - сетевой сокет, в частности IP-сокет. Обязательно посмотрите руководство Beej, которое я опубликовал, а затем проверьте свой код для сравнения. Надеюсь, это поможет! - person Josh; 15.07.2011
comment
Ну вот что я имел в виду - я изменил их обоих. Читаю ссылку сейчас. - person Sterling; 15.07.2011

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

  • сделать слушающий сокет неблокирующим
  • добавьте его в select(2) или _ 2_ прочитать набор событий
  • введите _3 _ / _ 4_ цикл
  • on wakeup check if it's the listening socket, then
    • accept(2)
    • проверить на сбой (клиент, возможно, уже прервал попытку подключения)
    • сделать вновь созданный клиентский сокет неблокирующим, добавить его в набор событий опроса
  • else, if it's one of the client sockets
    • consume input, process it
    • следите за кодом ошибки EAGAIN - на самом деле это не ошибка, а указание на отсутствие ввода сейчас
    • если прочитано ноль байтов - клиентское соединение закрыто, close(2) клиентский сокет, удалить его из набора событий
  • повторная инициализация набора событий (опускание этого является распространенной ошибкой с select(2))
  • повторить цикл

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

person Nikolai Fetissov    schedule 14.07.2011

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

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

person pm100    schedule 14.07.2011
comment
+1 для проверки того, что fcntl для установки действительно работает. Всегда проверяйте возвращаемое значение системных вызовов. Всегда. - person Nemo; 15.07.2011
comment
Я установил неблокирующий режим сразу после того, как получу его через вызов accept. Если я устанавливаю серверный сокет sock на неблокирующий, это дает мне ошибку ресурса, временно недоступного, когда я пытаюсь вызвать accept. - person Sterling; 15.07.2011

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

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

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

person shelman    schedule 14.07.2011