С розетки - заблокированы на выбор

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

В программе 2 объекта: мастер и клиент. Мастер знает количество клиентов, с которыми он будет работать, и ожидает подключения клиентов к нему. Как только он получает подтверждение клиента, он сохраняет свою информацию. Когда все клиенты подключены, он отправляет информацию о соседнем клиенте каждому клиенту, чтобы сформировать сеть. Именно здесь, я использую select() для мониторинга многих сокетов, мастер имеет сокет, к которому подключен каждый дочерний тат, клиент имеет 3 основных сокета s1, чтобы говорить с мастером, s2-ребенок прослушивает соединение по этому соседнему сокету. сокет, на котором его сосед ожидает подключения.(т. е. S2 в соседнем узле) p- сокет, который является результатом соединения от своего соседа (принимает s2 - возвращает ths). Я использую select для прослушивания сервера, свой собственный сокет для входящего соединения и один раз.

Первоначально мой сервер отправляет строку "привет" одному из клиентов, который получает это сообщение и передает его соседу, таким образом, когда строка возвращается обратно к первому дочернему элементу, который получил это сообщение от сервера, он передает его на своего соседа. Но все, хотя все дочерние элементы находятся в select(), ожидая ввода. Что может вызвать это??

void main(int argc, char **argv) {

    int s1, s2, n, server_port, sc1, sc2, rv, rc, left_peer_port;
    int peer_port;
    fd_set writefds, readfds;
    struct timeval tv;
    struct hostent *server_info, *child_info, *left_peer_info;
    int start_flag = 0;

    struct sockaddr_in server, peer, incoming;

    char host_child[64];
    char *left_host = malloc(1);
    char *right_host = malloc(1);
    char buf1[256];
    char buf2[256];

    server_port = atoi(argv[2]);

    //speak to peer using this
    s2 = socket(AF_INET, SOCK_STREAM, 0);
    if (s2 < 0) {
        perror("socket:");
        exit(s2);
    }

    peer_port = server_port + 1;

    gethostname(host_child, sizeof host_child);

    child_info = gethostbyname(host_child);

    if (child_info == NULL) {
        fprintf(stderr, "%s: host not found (%s)\n", argv[0], host_child);
        exit(1);
    }


    peer.sin_family = AF_INET;
    memcpy(&peer.sin_addr, child_info->h_addr_list[0], child_info->h_length);
    int changeport = 0;
    do {
        peer.sin_port = htons(peer_port);
        rc = bind(s2, (struct sockaddr *) &peer, sizeof(peer));

        if (rc < 0) {
            //perror("bind:");
            peer_port++;
            changeport = 1;
            //exit(rc);

        } else {
            changeport = 0;
        }

    } while (changeport == 1);

    if (listen(s2, 100) == -1) {
        perror("listen");
        exit(3);
    }



//Now talk to server

    server_info = gethostbyname(argv[1]);

    if (server_info == NULL) {
        fprintf(stderr, "%s: host not found\n", argv[0]);
        exit(1);
    }


// pretend we've connected both to a server at this point
//speak to server using this
    s1 = socket(AF_INET, SOCK_STREAM, 0);
    if (s1 < 0) {
        perror("socket:");
        exit(s1);
    }


    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    memcpy(&server.sin_addr, server_info->h_addr_list[0], server_info->h_length);

//To talk to the server

    sc1 = connect(s1, (struct sockaddr *) &server, sizeof(server));
    if (sc1 < 0) {
        perror("connect:");
        exit(sc1);
    }

    int send_len;
    char *str = malloc(1);
    sprintf(str, "%d", peer_port);
    printf("\nport-here=%s\n", str);


    send_len = send(s1, str, strlen(str), 0);
    if (send_len != strlen(str)) {
        perror("send");
        exit(1);
    }

    int recv_len;
    char buf[100];
    int ref = 0;
    int recv_stage = 0;
    int start_id;


    recv_len = recv(s1, buf, 34, 0);
    if (recv_len < 0) {
        perror("recv");
        exit(1);
    }
    buf[recv_len] = '\0';
    char *temp_port;

    if (!strcmp("close", buf))
        printf("%s", buf);
            //break;
    else {
        char *temp_buffer = malloc(1);
        char *id = malloc(100);
        char *pp = malloc(1);
        strcpy(temp_buffer, buf);

        char *search = ":";
        temp_port = strtok(temp_buffer, search);
        strcpy(buf, temp_port);
        printf("temp_name%s", temp_port);

        temp_port = strtok(NULL, search);
        strcpy(pp, temp_port);
        printf("temp_port%s", temp_port);
        temp_port = strtok(NULL, search);
        strcpy(id, temp_port);
        printf("id%s", temp_port);

        strcpy(temp_port, pp);
        printf("\nbuf=%s\n", buf);
        printf("\nport=%s\n", temp_port);
        printf("\nid=%s\n", id);
        start_id = atoi(id);
    }

//To send packet to its neighbour
    left_peer_info = gethostbyname(buf);
    printf("\nleft host=%s\n", buf);
    if (left_peer_info == NULL) {
        fprintf(stderr, "%s: host not found\n", left_host);
        exit(1);
    }
    left_peer_port = atoi(temp_port);


    int neighbour_socket;
    struct hostent *neighbour_info;
    struct sockaddr_in neighbour;
    neighbour_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (neighbour_socket < 0) {
        perror("socket:");
        exit(neighbour_socket);
    }

    neighbour_info = left_peer_info;


    neighbour.sin_family = AF_INET;
    neighbour.sin_port = htons(left_peer_port);
    memcpy(&neighbour.sin_addr, neighbour_info->h_addr_list[0], neighbour_info->h_length);

    printf("\nconnected to port %d\n", left_peer_port);
    //To talk to the neighbour
    printf("\ncomes here\n");


//Listen on this socket connection for potato

    int send_peer_len;
    int nfds;

    nfds = MAX(MAX(neighbour_socket, s2), s1);

// clear the set ahead of time
    FD_ZERO(&writefds);

// add our descriptors to the set
    FD_SET(neighbour_socket, &writefds);
    FD_SET(s1, &writefds);
    FD_SET(s2, &writefds);

//FD_SET(s2, &writefds);


    FD_ZERO(&readfds);
    FD_SET(neighbour_socket, &readfds);
    FD_SET(s1, &readfds);
    FD_SET(s2, &readfds);

//select()

// since we got s2 second, it's the "greater", so we use that for
// the n param in select()
//n = s1 + 1;

// wait until either socket has data ready to be recv()d (timeout 10.5 secs)
    tv.tv_sec = 10;
    tv.tv_usec = 500000;

    int fds[3];
    fds[0] = s1;
    fds[1] = s2;
    fds[2] = neighbour_socket;
    int p = 0;
    int p_flag = 0;

    while (1) {
        printf("\n nfds = %d , p = %d \n", nfds, p);
        char buf_msg[64];


        //This is where the error occurs  //

        rv = select(nfds, &readfds, NULL, NULL, 0);

        //This is where the error occurs  //

        if (rv == -1) {
            perror("select"); // error occurred in select()
        } else if (rv == 0) {
            printf("Timeout occurred!  No data after 10.5 seconds.\n");
        } else {
            // one or both of the descriptors have data
            //reading message from server
            int select_fd;
            for (select_fd = 0; select_fd <= nfds; select_fd++) {

                if (FD_ISSET(select_fd, &readfds) != 0) {
                    if (select_fd == s1) {

                        recv_len = 0;
                        recv_len = recv(s1, buf_msg, 34, 0);
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from server = %s\n", buf_msg);
                        //send to neighbour

                        int sc3;
                        sc3 = connect(neighbour_socket, (struct sockaddr *) &neighbour, sizeof(neighbour));
                        if (sc3 < 0) {
                            perror("connect:");
                            exit(sc3);

                        }

                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - s1  - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }
                        start_flag = 1;
                        //FD_CLR(s1, &readfds);

                        printf("\ncrossed server\n");

                    } else if (select_fd == s2) {

                        int list_len = sizeof incoming;
                        printf("\ninside client\n");
                        printf("\nWaiting for accept in S2\n");
                        if (p_flag == 0) {
                            p_flag = 1;
                            p = accept(s2, (struct sockaddr *) &incoming, &list_len);
                            printf("\nConnection accepted in S2\n");
                            if (p < 0) {
                                perror("bind:");
                                exit(rc);
                            }
                        }
                        nfds = MAX(nfds, p);
                        recv_len = 0;
                        buf_msg[recv_len] = '\0';
                        recv_len = recv(p, buf_msg, 34, 0);
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from client = %s\n", buf_msg);
                        //send to neighbour

                        //if(start_id!=1){
                        int sc3;
                        sc3 = connect(neighbour_socket, (struct sockaddr *) &neighbour, sizeof(neighbour));
                        if (sc3 < 0) {
                            perror("connect:");
                            //exit(sc3);
                        }
                        //}

                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - s2  - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }

                    } else if (select_fd == neighbour_socket) {

                        printf("\ncomes in\n");

                    } else if (select_fd == p && p != 0) {

                        int list_len = sizeof incoming;
                        printf("\ninside p\n");
                        recv_len = 0;
                        buf_msg[recv_len] = '\0';
                        printf("\nwaiting at recv in P\n");

                        recv_len = recv(p, buf_msg, 34, 0);

                        printf("\ncrossed at recv in P\n");
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from client = %s\n", buf_msg);
                        //send to neighbour
                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - neighbour - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }
                    }
                }
            }

            FD_ZERO(&readfds);
            //FD_SET(neighbour_socket,&readfds);
            FD_SET(s1, &readfds);
            FD_SET(neighbour_socket, &readfds);

            if (p_flag == 1) {
                printf("\nsetting P\n");
                FD_SET(p, &readfds);
                FD_SET(s2, &readfds);

                p_flag = 0;
            } else {
                printf("\nNot setting P\n");
                FD_SET(s2, &readfds);
            }
        }
    }
    close(s1);
    close(s2);
} 

Заранее спасибо.

3 ответа

Я верю, что Мэт нашел основную проблему. Но я думаю, что здесь есть гораздо большая проблема:

int send_len;
char *str=malloc(1);
sprintf(str,"%d",peer_port);
printf("\nport-here=%s\n",str);

Вы испортили свою кучу sprintf(3) вызов. Может быть, это не важные данные, которые вы перезаписали, и, возможно, malloc(3) фактически никогда не выделит один байт, но это неверное предположение. Вам нужно выделить как минимум шесть байтов для номера порта: пять для цифр в 65535 и один для замыкающего ASCII NUL\0 байт.

  buf[recv_len] = '\0';
  char *temp_port;
  //printf("\n-%s\n",buf);
  if ( !strcmp("close", buf) )
        printf("%s",buf);
    //break;
  else{
    char *temp_buffer=malloc(1);
    char *id=malloc(100);
    char *pp=malloc(1);
    strcpy(temp_buffer,buf);

В предыдущем выборе вы сохранили \0 в конце buf так что вы, вероятно, работаете с какой-то строкой. Но в нескольких строках вы выделяете один байт и затем копируете содержимое buf в этот единственный байт. ASCII NUL будет использовать этот байт полностью, не оставляя места для строки, которую вы получили. Но strcpy(3) не работает таким образом - он будет копировать содержимое buf до этого '\0' символ, в память, начиная с вашего одного байта. Вы снова уничтожили свою кучу. Но в этот раз он может перезаписать значительно больше пяти байтов - и все под контролем удаленного узла.

Переполнение кучи чрезвычайно опасно. Я нашел более 350 ссылок на уязвимости, которые можно использовать в программах, которые напрямую связаны с переполнением кучи, в старом архиве, который у меня есть в проекте Common Vulnerabilities and Exposures.

Не развертывайте эту программу на общедоступных компьютерах, пока не устраните эти проблемы. Это существенный недостаток безопасности. Найти каждый экземпляр malloc(1) и замените его правильным количеством памяти, которое должно быть выделено. Как только вы это сделаете, пожалуйста, запустите вашу программу с MALLOC_CHECK_=1 или под контролем valgrind(1) чтобы помочь вам найти дальнейшие проблемы выделения памяти.

Первый параметр для select должен быть максимальный дескриптор файла плюс один. Насколько я могу судить по тому огромному куску кода, который вы разместили, вы забыли это "плюс один".

Рассматривали ли вы использовать poll() вместо select()? Проще отладить и элегантно масштабировать столько, сколько вам нужно.

Другие вопросы по тегам