2013-11-30 2 views
0

У меня есть tcp-эхо-сервер, который создает pthread для каждого клиента, который подключается к нему. Для каждого соединения у меня есть переменная nbOfClients, которая увеличивается. Когда клиент закрывает свое соединение, я обнаруживаю его и уменьшаю количество клиентов. Однако сервер продолжает думать, что клиент жив и продолжает читать/писать из сокета. Я догадался, что это из-за потока, который создал клиента, и я пытаюсь убить поток с pthread_cancel все, чтобы не воспользоваться. Я хочу убить pthread, связанный с определенным клиентом, который закрывает его соединение. Как я могу это сделать?Как правильно завершить pthread?

Вот мой код:

static int nbOfClients = 0; 

static pthread_t tid; 

int main (int argc, char *argv[]) { 

    int bytes_to_read, arg, listen_sd, new_conn, sockfd, client_len, port; 
    struct sockaddr_in server, client_addr; 
    char *bp, buf[BUFLEN]; 
    ssize_t n; 


    sockfd = 0; 

    switch(argc) { 
     case 1: 
      port = SERVER_TCP_PORT; // Use the default port 
      break; 
     case 2: 
      port = atoi(argv[1]); // Get user specified port 
      break; 
     default: 
      fprintf(stderr, "Usage: %s [port]\n", argv[0]); 
      exit(1); 
    } 

    // Create a stream socket 
    if ((listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
     error("Cannot Create Socket!"); 

    // set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c 
    arg = 1; 
    if (setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1) 
     error("setsockopt"); 

    // Bind an address to the socket 
    bzero((char *)&server, sizeof(server)); 
    server.sin_family = AF_INET; 
    server.sin_port = htons(port); 
    server.sin_addr.s_addr = htonl(INADDR_ANY); // Accept connections from any client 

    if (bind(listen_sd, (struct sockaddr *)&server, sizeof(server)) == -1) 
     error("bind error"); 

    listen(listen_sd, MAX_CONNECTIONS); ///put a define constant indicating the maximum number of clients #define NB_CLIENTS 3 

    while (TRUE) { 
     client_len = sizeof(client_addr); 
     if ((new_conn = accept(listen_sd, (struct sockaddr *) &client_addr, (socklen_t *)&client_len)) == -1) 
      error("accept error"); 

     if(new_conn > 0) { 
      if(nbOfClients < MAX_CONNECTIONS) { 
       printf("just here\n"); 
       printf(">> Initializing remote address: %s\n", inet_ntoa(client_addr.sin_addr)); 
       nbOfClients++; 


       fclose(fp); 

       printf("Connections to date: %u \n",nbOfClients); 

       printf("make thread\n"); 
       pthread_create(&tid,NULL,&echo, (void *)new_conn); 
       printf("had thread\n"); 
      } 
      else { 
       printf("connection limit reached\n"); 
       if(send(new_conn, "Server full!\n", 13, 0) == -1) 
        perror("send"); 
       close(new_conn); 
      } 
     } 
    } 

    return(0); 
} 

void * echo(void *arg) { 
    char buf[BUFSIZE]; /* message buffer */ 
    int n, i = 0; 

    bzero(buf, BUFSIZE); 
    if(send((int)arg, "Welcome!!\n", 20, 0) == -1) 
     perror("send"); 

    detect_closed_connection(arg); 

    while(TRUE) { 
     n = read((int)arg, buf, BUFSIZE); 

     /**read: read input string from the client*/ 
     if(n < 0) { 
      perror("error reading from socket"); 
     } 

     printf("Server received from client, %d bytes: %s\n", n, buf); 

     /**write: echo the input string in UPPERCASE back to the client*/ 

     int len = strlen(buf); 
     for(i = 0; buf[i]; i++) 
      buf[i] = toupper(buf[i]); 

     n = write((int)arg, buf, len); 
     if(n < 0) { 
      error("ERROR writing to socket"); 
     } 
    } 
} 

void detect_closed_connection(void * listenSocket) { 
    struct pollfd pfd; 
    pfd.fd = (int)listenSocket; 
    pfd.events = POLLIN | POLLHUP | POLLRDNORM; 
    pfd.revents = 0; 
    while(pfd.revents == 0) { 
     if(poll(&pfd, 1, 100) > 0) { 
      // if result > 0, this means that there is either data available on the 
      // socket, or the socket has been closed 
      char buffer[32]; 
      if (recv((int)listenSocket, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) { 
       // if recv returns zero, that means the connection has been closed: 

       nbOfClients--; 
       pthread_cancel(tid); 

      } 
     } 
    } 
} 

Спасибо.

+0

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

+0

Я обнаруживаю это в бесконечной эхо-петле. detect_closed_connection (Arg); Означает ли это, что перед входом в цикл я должен обнаружить закрытое соединение? Я не уверен, что если положить его в основную функцию сразу после создания потока, будет нормально функционировать – mkab

+0

«Я обнаруживаю это в бесконечном эхо-цикле.» «Нет, нет, вы вызываете' detect_closed_connection (arg); '* * до ** 'while (TRUE'), который является главой бесконечного цикла. Нить никогда больше не идет перед этой линией. – alk

ответ

1

Вы должны проверить read() для возвращения 0 в потоке servering клиента, так как read() возвращает 0 в случае равноправный (клиент здесь) закрыл соединение.

После этой линии

n = read((int)arg, buf, BUFSIZE); 

добавить

if (0 == n) 
{ 
    fprintf(stderr, "The client closed the connection.\n"); 
    break; 
} 

Непосредственно перед тем, функция потока оставить можно добавить заявление для уменьшения числа запущенных потоков.


Также быть в курсе, что nbOfClients доступ concurently всеми «клиент» -threads, а также основным потоком, поэтому доступ к ней должен быть защищен, например, с помощью мьютекса.


Существует еще вопросы, как призыв к strlen() на буфера чтения ожидает, что буфер будет 0 -terminate, который не обязательно нуждается в ВЗ быть, даже если вы послали 0 -завершённый «струны» , read() может очень хорошо вернуть «строку», отправленную клиентом в более чем одной части. Итак, петля вокруг read() до тех пор, пока не был получен 0 -терминатор.


Не заставляйте конец нити само по телефону pthread_cancel(), используйте pthread_exit() вместо этого.

+0

Да, отлично! Я не думал о мьютексе на nbOfClients. Я должен определенно защитить доступ к nbOfClients. О чтении, что объясняет многое о том, почему сервер продолжает возвращать части предыдущих слов, отправленных клиентом. Спасибо за объяснение. – mkab

+0

@mkab: Мой совет по циклу вокруг 'read()' применяется также к 'write()'.Внимательно прочитайте man-страницы о том, что возвращает эти функции, если они вызываются в сокетах. Это также относится к 'recv()' и 'send()'. – alk

+0

Если я хорошо понимаю, чтобы читать и записывать данные из сокетов с помощью 'read()' и 'write()', я должен перебирать буфер до тех пор, пока не столкнутся с символом '' \ 0''? То же самое относится к 'recv()' и 'send()'? – mkab

Смежные вопросы