简体   繁体   中英

Using select() in thread does not react os host disconnection

I have main server code, which uses select and waits for hosts activities. I have also global structure, which stores all clients and theirs sockets.

//old code

Select in main thread unblocks intself when one of the hosts from clients array disconnects. Then I try to read data, and when it is 0, client is disconnected. Why select in Thread doesn't work in the same way? It works perfect when clients exchange data, but there is no reaction, when one of clients disconnects.

EDIT.

Sorry I have previously posted the code, which could not be compiled. The idea was, just to get the information if my idea of clients handling is correct and if this idea should work. I have created simple example and now it works. So, I have to review my oryginal code.

Working example:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include <pthread.h>
#include <limits.h>
#include <errno.h>
#include <time.h>
#include <sys/wait.h>

#define MAXCLIENTS 1000
#define FRAMESIZE 40

struct Client
{
  int socket;
  char state;
};
struct Client clients[2];

struct ThreadArgs
{
  int srcClientIdx;
  int dstClientIdx;
};

int portNumber=8090;

void * SocketThread(void *args)
{
   printf("New thread created.\n");
   struct ThreadArgs *threadArgs = (struct ThreadArgs*)args;
   int sidx = threadArgs->srcClientIdx;
   int didx = threadArgs->dstClientIdx;
   int srcSocket = clients[sidx].socket;
   int dstSocket = clients[didx].socket;

   fd_set readfds;
   struct timeval timeout;

   while(1)
   {
      FD_ZERO(&readfds);
      FD_SET(srcSocket, &readfds);
      FD_SET(dstSocket, &readfds);

      printf("Waiting for activities in thread.\n");

      timeout.tv_sec = 60;
      timeout.tv_usec = 0;
      int activity = select(MAXCLIENTS+1, &readfds , NULL , NULL , &timeout);
      if ((activity < 0) && (errno!=EINTR))
         printf("Activity error.\n");
      else if(activity == 0)
         printf("Activity timeout.\n");

      if (FD_ISSET(srcSocket, &readfds))
      {
         unsigned char buffer[FRAMESIZE];
         int bytesCnt = read(srcSocket, buffer, sizeof(buffer));
         if(bytesCnt == 0)
         {
           printf("Client%d disconnected.\n",sidx);
           clients[sidx].state = 0;
           clients[sidx].socket = -1;
           pthread_exit(NULL);
         }
         else
           printf("Client%d activity.\n",sidx);
      }

      if (FD_ISSET(dstSocket, &readfds))
      {
         unsigned char buffer[FRAMESIZE];
         int bytesCnt = read(dstSocket, buffer, sizeof(buffer));
         if(bytesCnt == 0)
         {
           printf("Client%d disconnected.\n",didx);
           clients[didx].state = 0;
           close(clients[didx].socket);
           clients[didx].socket = -1;
           pthread_exit(NULL);
         }
         else
           printf("Client%d activity.\n",didx);
      }

   }
 }

 int ConfigureTCPIPconnection(int socket,struct sockaddr_in *serverAddr)
 {
   // Address family = Internet
   serverAddr->sin_family = AF_INET;
   //Set port number, using htons function to use proper byte order
   serverAddr->sin_port = htons(portNumber);
   //Incoming addresses
   serverAddr->sin_addr.s_addr = INADDR_ANY;

   //Set all bits of the padding field to 0
   memset(serverAddr->sin_zero, '\0', sizeof serverAddr->sin_zero);

   //Bind the address struct to the socket
   if(bind(socket, (struct sockaddr *)serverAddr, sizeof(struct sockaddr)) < 0)
   {
     printf("Binding failed. %s\n",strerror(errno));
     return 1;
   }

   printf("Bind sucessfull.\n");
   return 0;
 }

int CheckSocketActivity(fd_set *readfds)
{
   int activity = select( MAXCLIENTS + 1 , readfds , NULL , NULL , NULL);
   if ((activity < 0) && (errno!=EINTR))
      return 1;
   return 0;
}

int main(int argc, char *argv[])
{
  fd_set readfds;
  int serverSocket,newSocket;
  pthread_t td; //thread descriptor
  struct sockaddr_in serverAddr, newAddr={0};
  socklen_t addr_size;

  clients[0].state = 0;
  clients[0].socket = -1;
  clients[1].state = 0;
  clients[1].socket = -1;

  if((serverSocket = socket(PF_INET, SOCK_STREAM, 0)) == 0)
  {
    printf("Server socket creation failed.\n");
    exit(1);
  }

  if(ConfigureTCPIPconnection(serverSocket,&serverAddr))
  {
    printf("Binding failed.\n");
    exit(2);
  }

  if(listen(serverSocket,MAXCLIENTS))
  {
    printf("Listen error.\n");
    exit(3);
  }

  printf("Listening...\n");

  while(1)
  {
    FD_ZERO(&readfds); //clear fd set
    FD_SET(serverSocket, &readfds); //add server socket to fd set
    if(clients[0].state == 1)
      FD_SET(clients[0].socket, &readfds); //add active clients to fd set
    if(clients[1].state == 1)
      FD_SET(clients[1].socket, &readfds);

    printf("Waiting for activities.\n");

    if(!CheckSocketActivity(&readfds))
    {
      if(FD_ISSET(serverSocket, &readfds))
      {
        if((newSocket = accept(serverSocket, (struct sockaddr *)&newAddr, (socklen_t*)&addr_size))<0) //create new socket
          printf("New socket error. %s\n",strerror(errno));
        else
        {
          printf("New socket connected.\n");
          if(clients[0].state == 0)
          {
            printf("Client0 added.\n");
            clients[0].state = 1;
            clients[0].socket = newSocket;
          }
          else if(clients[1].state == 0)
          {
            printf("Client1 added.\n");
            clients[1].state = 1;
            clients[1].socket = newSocket;
          }
        }
      }
        else
        {
           for(int i=0;i<2;i++)
           {
              int srcSock = clients[i].socket;
              if (FD_ISSET(srcSock, &readfds))
              {
                 unsigned char buffer[FRAMESIZE];
                 int bytesCnt = read(srcSock, buffer, sizeof(buffer));
                 if(bytesCnt == 0)
                 {
                   printf("Client%d disconnected.\n",i);
                   clients[i].state = 0;
                   close(clients[i].socket);
                   clients[i].socket = -1;
                 }
                 else
                 {
                   if(clients[0].state == 1 && clients[1].state == 1)
                   {
                      int srcIndex,dstIndex;
                      //some other stuff
                      clients[0].state = 2;
                      clients[1].state = 2;
                      if(i == 0)
                      {
                        srcIndex = 0;
                        dstIndex = 1;
                      }
                      else
                      {
                        srcIndex = 1;
                        dstIndex = 0;
                      }
                      printf("Creating new thread.\n");
                      struct ThreadArgs threadArgs = {srcIndex,dstIndex};
                      if(pthread_create(&td, NULL, SocketThread, &threadArgs) != 0)
                         printf("Failed to create thread.\n");
                      if (pthread_detach(td))
                         printf("Thread detach error.\n");
                    }
                    else
                      printf("Two clients required for creating thread.\n");
                 }
              }
           }
        }
     }
  }
}

I assume you posted the code you are indeed dealing with because the code you posted shouldn't compile.

  • Correction 1 (May not be the source of the problem)

From the select() man page:

void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set); 

All FD_*() macros take fd_set* argument. In the main() you have passed fd_set instead of fd_set* at few places.

  • Correction 2 (May be the source of the problem)

In SocketThread() this piece of code might be the cause of the issue:

  if (FD_ISSET(dstSocket, &readfds))
   {
      unsigned char buffer[FRAMESIZE];
      bytesCnt = read(srcSock, buffer, sizeof(buffer));
      if(bytesCnt == 0)
         **//this is not detected**
      else
        //any other activities are detected
   }

You may probably want read(dstSocket, buffer, sizeof(buffer)); .

You say:

Select in main thread unblocks intself when one of the hosts from clients array disconnects. Then I try to read data, and when it is 0, client is disconnected. Why select in Thread doesn't work in the same way? It works perfect when clients exchange data, but there is no reaction, when one of clients disconnects.

Please, you don't say how the client disconnects. Does it just close the socket? does it do a shutdown? I don't see any close(2) on the socket, so why do you know who is closing the connection (or calling shutdown(2) to indicate that no more data is going to appear in the connection)?

normally, a connection blocks until you receive some data... receiving 0 data means no data has arrived or, after a select(2) call has selected a socket descriptor that show no more data, then you have the socket closed. But this has to be closed somewhere, and you show neither close(2) nor shutdown(2) . You also don't describe what is what is detected on the socket. If you awake from the select(2) you get to the file descriptor and the result from read is not 0 what value does the read return? have you got acquainted that -1 is one of such possible values, and that it means you have a readin error? Do you check the value returned by select? (on timeout, my memory tells me that select(0) returns 0, so are you checking for timeout?)

Please, next time, provide a complete and verifiable example. The code you post cannot be made to run without a huge amount of contribution, so the problem can only be reproduced on your side. Check this page for some reading about how to post code in StackOverflow.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM