Вопрос по c, sockets – Включить неблокирующий сокет

0

У меня есть сервер, написанный на C / C ++. Я установил обертку для соединения следующим образом:

    //START WRAPPER
    void Server::init_address(int port)
    {
memset(&(this->serv_addr), 0, sizeof(this->serv_addr));
this->serv_addr.sin_family = AF_INET;
this->serv_addr.sin_port = htons(port);
this->serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }


    int Server::w_socket()
    {
int retv;
retv = socket(PF_INET, SOCK_STREAM, 0);
//FIXME
//fcntl(retv, F_SETFL, O_NONBLOCK);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[socket] " + err_msg;
    throw err_msg;
}
else
{
    int reuse_opt = 1;

    if(setsockopt(retv, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(int))==-1)
    {
        perror("setsockopt error");
        exit(1);
    }
    return retv;
}
    }

    void Server::w_bind()
    {
int retv;
retv = bind(this->sock_fd,
        (struct sockaddr*) &(this->serv_addr),
        sizeof(this->serv_addr));

if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[bind] " + err_msg;
    throw err_msg;
}
    }

    void Server::w_listen()
    {
int retv;
retv = listen(this->sock_fd, 3);
if (retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[listen] " + err_msg;
    throw err_msg;
}
    }

    int Server::w_accept(struct sockaddr_in* client_addr)
    {
int retv;
int socklen = sizeof(sockaddr_in);

retv = accept(this->sock_fd, (struct sockaddr*)client_addr, (socklen_t*)&socklen);
if(retv == -1)
{
    std::string err_msg(strerror(errno));
    err_msg = "[accept] " + err_msg;
    throw err_msg;
}
else
{
    return retv;
}
            }

    int Server::recvtimeout(int s, char *buf, int len, int timeout)
    {
fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
    return -2; // timeout!
}
if (n == -1){
    return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
    }
    // END WRAPPER

Моя цель - включить неблокирующий режим сокета. Я пытался сделатьfcntl(retv, F_SETFL, O_NONBLOCK); как сказал руководство Beej, но я получаю ошибку:[accept] Resource temporarily unavailable Решением этой проблемы является использованиеselect функция, но я уже использую ее в функции recvtimeout, всегда, как сказал гид Beej.

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

Ваш Ответ

2   ответа
5

Вы получаете ошибкуbecause розетка неблокирующая.

Либо вы делаете занятый цикл, пока вы получаете эту ошибку (проверьтеerrno когдаaccept возвращается-1 для любогоEWOULDBLOCK или жеEAGAIN). Другое и рекомендуемое решение заключается в использованииselect чтобы увидеть, когда сокет читается, вы можете позвонитьaccept.

Edit: Как это сделать сselect

Вы должны иметь цикл обработки событий на более высоком уровне, который проверяет, можно ли читать прослушивающий сокет или подключенные сокеты. Если прослушиваемый сокет доступен для чтения, вы можете принять новое соединение, а если подключенные сокеты доступны для чтения, вы можете прочитать их.

Что-то вроде:

for (;;)
{
    fd_set readset;

    FD_ZERO(&readset); 
    FD_SET(listening_socket, &readset);
    int maxfd = listening_socket;

    if (connected_socket >= 0)
    {
        FD_SET(connected_socket, &readset);
        maxfd = max(maxfd, connected_socket);
    }

    // NULL timeout (5-th argument) means wait until event
    select(maxfd + 1, &readset, NULL, NULL, NULL);

    if (FD_ISSET(listening_socket, &readset))
    {
        accept_new_connection(listening_socket);
    }

    if (connected_socket >= 0 && FD_ISSET(connected_socket, &readset))
    {
        if (!read_from_socket(connected_socket))
        {
            close(connected_socket);
            connected_socket = -1;
        }
    }
}

Если у вас есть несколько соединенных сокетов, поместите их в простой связанный список и добавьте / проверьте их в цикле. Удалить из списка, когда они закрыты.

Хорошо, последний вопрос. В моем случае, я могу изменить функцию recvtimeout или сделать другую функцию, выбрав другую? Потому что оба они высокого уровня, когда я вызываю получить. user840718
Ну, я проверил errno на EAGAIN, как в этом примере, на функции accept с помощью оператора do while,stackoverflow.com/questions/735249/… Я раскомментировал fcntl (retv, F_SETFL, O_NONBLOCK); Теперь все вроде бы работает, но разве неблокирует? Я установил свой сервер, и он все еще не работает. О втором решении использовать select, чтобы увидеть, когда сокет доступен для чтения, как я могу это использовать? Моего выбора в функции recvtimeout недостаточно для этого? Благодарю. user840718
1

В вашем решении вы используетеselect вызовите сокет подключения, но вы не используете его дляlisten socket.

Если вы изменилиlisten socket также для неблокирования, то вы должны использовать выбор дляlisten socket перед звонкомaccept.

Похожие вопросы