反思:关于select的一些经历

来源:互联网 发布:java笛卡尔积算法 编辑:程序博客网 时间:2024/05/21 15:07

今天交了,该了几改的程序。是一个服务器转发聊天程序,第一次是在别人例子的基础上直接做得。后来,发现我用得时阻塞,当时考虑的问题太多。包括一个包分了好几次才收到,或者一次收到了,相互独立且完整的几个包。

最重要的一个问题是,在使用费阻塞的io以后,因为对select的FD_ISSET理解错误(我认为是不是变化,但事实上是不是在集合内),我对select返回可读套接字后能不能一定读到数据产生了疑问。我相信了直觉。用了一个结构体,里面分别放上recvbuffer[1024]和已经接受的数据的大小。并且在接受数据的时候使用for(;;),于是出现了如下的变态函数。

void recvFromClient(int m, struct targetPack *ss, int *testint, char *content){int recvsize;int totalsize;char *ptr;totalsize = 1024;ptr = conns[m].totalBuffer;memset(ss,0,sizeof(struct targetPack));//memset(conns[m].totalBuffer,0,1024);for(;;){recvsize = 0;recvsize = recv(conns[m].clientfd, ptr, totalsize-conns[m].edSize, MSG_DONTWAIT);if(recvsize > 0){ptr += recvsize;conns[m].edSize += recvsize;printf("Received %d bytes\n",recvsize);}else if(recvsize < 0) {            if(errno == EAGAIN)             {                    errno=0;printf("errno == EAGAIN");break;                }            else            {                    perror("recv");                    exit(EXIT_FAILURE);                }    }else if(recvsize = 0){printf("client has discontent\n");break;}   if(conns[m].edSize >= border_size){memcpy(ss, conns[m].totalBuffer, sizeof(struct targetPack));printf("xml length is %d\n", ss->XmlLength);if((conns[m].edSize - border_size) > (ss->XmlLength)){char xmlstr[900];memset(xmlstr,'\0',900);memcpy(xmlstr, conns[m].totalBuffer+border_size,ss->XmlLength);char *file_name = "xmlFileFromClient.xml";generateTimeFile(file_name, xmlstr);char xmlbuf[300];getFDandContent(file_name, testint, xmlbuf);printf("content from client xml:\n%s\n", xmlbuf);sprintf(content, "%s", xmlbuf);conns[m].recverfd = *testint;}}else if(conns[m].edSize < 0){printf("conns[m].edSize < 0: %d\n", conns[m].edSize);break;}}}

其实,用非阻塞的select跟阻塞的在正常情况下基本差不多。只在网络或者系统资源紧张的时候才会出现不同。

但是非阻塞的io的话,以我现在的理解,貌似只是让客服端出现问题的时候服务器不会卡在那里。

我把程序写成这样唯一的好处是,如果客户端吧一个完整的数据包,分为大小不同的很多包。在不同的时间里发过来,服务器仍然能够收到。

说好听点,就是网络延时极高的话。我的程序仍然可以收到数据。

    unix环境高级编程和unix网络编程。有个问题,就是总是用他自己写的函数。估计他是想减少复杂性,却不知让人更不好理解。没仔细看过他给的例子,网上找得例子,要不太小儿科,要不没有一点注释。今天心血来潮用了下google

找到一个相对较好的程序,注释什么的也比较详细:

#include <stdio.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#include <errno.h>#define SERVER_PORT  12345#define TRUE             1#define FALSE            0main (int argc, char *argv[]){   int    i, len, rc, on = 1;   int    listen_sd, max_sd, new_sd;   int    desc_ready, end_server = FALSE;   int    close_conn;   char   buffer[80];   struct sockaddr_in   addr;   struct timeval       timeout;   struct fd_set        master_set, working_set;   /*************************************************************/   /* Create an AF_INET stream socket to receive incoming       */   /* connections on                                            */   /*************************************************************/   listen_sd = socket(AF_INET, SOCK_STREAM, 0);   if (listen_sd < 0)   {      perror("socket() failed");      exit(-1);   }   /*************************************************************/   /* Allow socket descriptor to be reuseable                   */   /*************************************************************/   rc = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,                   (char *)&on, sizeof(on));   if (rc < 0)   {      perror("setsockopt() failed");      close(listen_sd);      exit(-1);   }   /*************************************************************/   /* Set socket to be non-blocking.  All of the sockets for    */   /* the incoming connections will also be non-blocking since  */   /* they will inherit that state from the listening socket.   */   /*************************************************************/   rc = ioctl(listen_sd, FIONBIO, (char *)&on);   if (rc < 0)   {      perror("ioctl() failed");      close(listen_sd);      exit(-1);   }   /*************************************************************/   /* Bind the socket                                           */   /*************************************************************/   memset(&addr, 0, sizeof(addr));   addr.sin_family      = AF_INET;   addr.sin_addr.s_addr = htonl(INADDR_ANY);   addr.sin_port        = htons(SERVER_PORT);   rc = bind(listen_sd,             (struct sockaddr *)&addr, sizeof(addr));   if (rc < 0)   {      perror("bind() failed");      close(listen_sd);      exit(-1);   }   /*************************************************************/   /* Set the listen back log                                   */   /*************************************************************/   rc = listen(listen_sd, 32);   if (rc < 0)   {      perror("listen() failed");      close(listen_sd);      exit(-1);   }   /*************************************************************/   /* Initialize the master fd_set                              */   /*************************************************************/   FD_ZERO(&master_set);   max_sd = listen_sd;   FD_SET(listen_sd, &master_set);   /*************************************************************/   /* Initialize the timeval struct to 3 minutes.  If no        */   /* activity after 3 minutes this program will end.           */   /*************************************************************/   timeout.tv_sec  = 3 * 60;   timeout.tv_usec = 0;   /*************************************************************/   /* Loop waiting for incoming connects or for incoming data   */   /* on any of the connected sockets.                          */   /*************************************************************/   do   {      /**********************************************************/      /* Copy the master fd_set over to the working fd_set.     */      /**********************************************************/      memcpy(&working_set, &master_set, sizeof(master_set));      /**********************************************************/      /* Call select() and wait 5 minutes for it to complete.   */      /**********************************************************/      printf("Waiting on select()...\n");      rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);      /**********************************************************/      /* Check to see if the select call failed.                */      /**********************************************************/      if (rc < 0)      {         perror("  select() failed");         break;      }      /**********************************************************/      /* Check to see if the 5 minute time out expired.         */      /**********************************************************/      if (rc == 0)      {         printf("  select() timed out.  End program.\n");         break;      }      /**********************************************************/      /* One or more descriptors are readable.  Need to         */      /* determine which ones they are.                         */      /**********************************************************/      desc_ready = rc;      for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)      {         /*******************************************************/         /* Check to see if this descriptor is ready            */         /*******************************************************/         if (FD_ISSET(i, &working_set))         {            /****************************************************/            /* A descriptor was found that was readable - one   */            /* less has to be looked for.  This is being done   */            /* so that we can stop looking at the working set   */            /* once we have found all of the descriptors that   */            /* were ready.                                      */            /****************************************************/            desc_ready -= 1;            /****************************************************/            /* Check to see if this is the listening socket     */            /****************************************************/            if (i == listen_sd)            {               printf("  Listening socket is readable\n");               /*************************************************/               /* Accept all incoming connections that are      */               /* queued up on the listening socket before we   */               /* loop back and call select again.              */               /*************************************************/               do               {                  /**********************************************/                  /* Accept each incoming connection.  If       */                  /* accept fails with EWOULDBLOCK, then we     */                  /* have accepted all of them.  Any other      */                  /* failure on accept will cause us to end the */                  /* server.                                    */                  /**********************************************/                  new_sd = accept(listen_sd, NULL, NULL);                  if (new_sd < 0)                  {                     if (errno != EWOULDBLOCK)                     {                        perror("  accept() failed");                        end_server = TRUE;                     }                     break;                  }                  /**********************************************/                  /* Add the new incoming connection to the     */                  /* master read set                            */                  /**********************************************/                  printf("  New incoming connection - %d\n", new_sd);                  FD_SET(new_sd, &master_set);                  if (new_sd > max_sd)                     max_sd = new_sd;                  /**********************************************/                  /* Loop back up and accept another incoming   */                  /* connection                                 */                  /**********************************************/               } while (new_sd != -1);            }            /****************************************************/            /* This is not the listening socket, therefore an   */            /* existing connection must be readable             */            /****************************************************/            else            {               printf("  Descriptor %d is readable\n", i);               close_conn = FALSE;               /*************************************************/               /* Receive all incoming data on this socket      */               /* before we loop back and call select again.    */               /*************************************************/               do               {                  /**********************************************/                  /* Receive data on this connection until the  */                  /* recv fails with EWOULDBLOCK.  If any other */                  /* failure occurs, we will close the          */                  /* connection.                                */                  /**********************************************/                  rc = recv(i, buffer, sizeof(buffer), 0);                  if (rc < 0)                  {                     if (errno != EWOULDBLOCK)                     {                        perror("  recv() failed");                        close_conn = TRUE;                     }                     break;                  }                  /**********************************************/                  /* Check to see if the connection has been    */                  /* closed by the client                       */                  /**********************************************/                  if (rc == 0)                  {                     printf("  Connection closed\n");                     close_conn = TRUE;                     break;                  }                  /**********************************************/                  /* Data was recevied                          */                  /**********************************************/                  len = rc;                  printf("  %d bytes received\n", len);                  /**********************************************/                  /* Echo the data back to the client           */                  /**********************************************/                  rc = send(i, buffer, len, 0);                  if (rc < 0)                  {                     perror("  send() failed");                     close_conn = TRUE;                     break;                  }               } while (TRUE);               /*************************************************/               /* If the close_conn flag was turned on, we need */               /* to clean up this active connection.  This     */               /* clean up process includes removing the        */               /* descriptor from the master set and            */               /* determining the new maximum descriptor value  */               /* based on the bits that are still turned on in */               /* the master set.                               */               /*************************************************/               if (close_conn)               {                  close(i);                  FD_CLR(i, &master_set);                  if (i == max_sd)                  {                     while (FD_ISSET(max_sd, &master_set) == FALSE)                        max_sd -= 1;                  }               }            } /* End of existing connection is readable */         } /* End of if (FD_ISSET(i, &working_set)) */      } /* End of loop through selectable descriptors */   } while (end_server == FALSE);   /*************************************************************/   /* Cleanup all of the sockets that are open                  */   /*************************************************************/   for (i=0; i <= max_sd; ++i)   {      if (FD_ISSET(i, &master_set))         close(i);   }}


原创粉丝点击