Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9139580
  • 博文数量: 1725
  • 博客积分: 12961
  • 博客等级: 上将
  • 技术积分: 19840
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-09 11:25
个人简介

偷得浮生半桶水(半日闲), 好记性不如抄下来(烂笔头). 信息爆炸的时代, 学习是一项持续的工作.

文章分类

全部博文(1725)

文章存档

2024年(1)

2023年(26)

2022年(112)

2021年(217)

2020年(157)

2019年(192)

2018年(81)

2017年(78)

2016年(70)

2015年(52)

2014年(40)

2013年(51)

2012年(85)

2011年(45)

2010年(231)

2009年(287)

分类: 其他平台

2013-05-06 17:00:59

标签:
     keepalive机制的自定义    远端socket关闭造成send操作崩溃  非阻塞式的connect操作  实时检测socket上是否有错误


server.c 

点击(此处)折叠或打开

  1. /*
  2. * 监听, 收到内容变大写后回写
  3. */
  4. #include <stdio.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <sys/un.h>
  8. #include <errno.h>
  9. #include <unistd.h>
  10. #include <signal.h>
  11. #include <sys/wait.h>
  12. #include <netdb.h>

  13. void sig_handler(int signo)
  14. {
  15.  pid_t pid;
  16.  int stat;

  17.  pid=waitpid(-1,&stat,WNOHANG);
  18.  while(pid>0){
  19.   printf("child process terminated (PID: %ld)\n",(long)getpid());
  20.   pid=waitpid(-1,&stat,WNOHANG);
  21.  }

  22.  return;
  23. }


  24. int main(int argc,char *argv[])
  25. {
  26.  socklen_t clt_addr_len;
  27.  int listen_fd;
  28.  int com_fd;
  29.  int ret;
  30.  int i;
  31.  static char recv_buf[1024];
  32.  int len;
  33.  int port;
  34.  int sleep_cnt;
  35.  pid_t pid;

  36.  char sys_cmd[20];

  37.  struct sockaddr_in clt_addr;
  38.  struct sockaddr_in srv_addr;


  39.  port=5000;

  40.  if(signal(SIGCHLD,sig_handler)<0){
  41.   perror("cannot set the signal");
  42.   return 1;
  43.  }

  44.  listen_fd=socket(PF_INET,SOCK_STREAM,0);
  45.  if(listen_fd<0){
  46.   perror("cannot create listening socket");
  47.   return 1;
  48.  }

  49.  int flag=1;
  50.  len=sizeof(int);
  51.  setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &flag, len);

  52.  memset(&srv_addr,0,sizeof(srv_addr));
  53.  srv_addr.sin_family=AF_INET;
  54.  srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
  55.  srv_addr.sin_port=htons(port);

  56.  ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
  57.  if(ret==-1){
  58.   perror("cannot bind server socket");
  59.   close(listen_fd);
  60.   return 1;
  61.  }

  62.  ret=listen(listen_fd,5);
  63.  if(ret==-1){
  64.   perror("cannot listen the client connect request");
  65.   close(listen_fd);
  66.   return 1;
  67.  }
  68.  
  69.    
  70.  while(1)
  71.  {

  72.   len=sizeof(clt_addr);
  73.   com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
  74.   if(com_fd<0){
  75.    if(errno==EINTR){
  76.     continue;
  77.    }else{
  78.     perror("cannot accept client connect request");
  79.     close(listen_fd);
  80.     return 1;
  81.    }
  82.   }

  83.   pid=fork();
  84.   if(pid<0){
  85.    perror("cannot create the child process");
  86.    close(listen_fd);
  87.    return 1;
  88.   }else if(pid==0)
  89.   {
  90.  
  91.    printf("the child child process is %ld \n",(long)getpid());
  92.    while((len=recv(com_fd,recv_buf,1024,0))>0)
  93.    {
  94.     printf("Message from client(%d): %s\n",len,recv_buf);
  95.     if(recv_buf[0]=='@')
  96.     {
  97.      close(com_fd);
  98.      return 0;
  99.     }
  100.     for(i=0;i<len;i++)
  101.      recv_buf[i]=toupper(recv_buf[i]);
  102.     write(com_fd,recv_buf,len);
  103.    }

  104.    close(com_fd);
  105.    printf("return while(1) \n");
  106.    return 0;

  107.   }else
  108.   {
  109.    close(com_fd);
  110.    printf("the child parent process is %ld \n",(long)getpid());

  111.   }
  112.  }
  113.  
  114.  return 0;
  115. }
client.c 
使用非阻塞式的connect操作, 对网络异常和对端socket断开的情况有简单的处理。

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <errno.h>
  6. #include <sys/types.h>
  7. #include <sys/ioctl.h>
  8. #include <fcntl.h>
  9. #include <sys/select.h>
  10. #include <arpa/inet.h>
  11. #include <sys/socket.h>
  12. #include <netinet/tcp.h>
  13. #include <signal.h>

  14. int sockfd = -1;

  15. static int SockSelect(int sockfd, fd_set *readfs, fd_set *writefs, fd_set *excepfs, struct timeval *vtimeout)
  16. {
  17.     int iret;
  18.     while ((iret=select(sockfd+1, readfs, writefs, excepfs, vtimeout)) < 0)
  19.     {
  20.         if (errno==EINTR) continue;
  21.         else break;
  22.     }

  23.     return iret;
  24. }

  25. int check_sock_error(int s)
  26. {/*
  27.     fd_set exceptSet;
  28.     struct timeval vtimeout;
  29.     
  30.     FD_ZERO(&exceptSet);
  31.     FD_SET(s, &exceptSet);
  32.     
  33.     vtimeout.tv_sec = 0;
  34.     vtimeout.tv_usec = 100*1000;
  35.     
  36.     int iRet =SockSelect(s , NULL, NULL, &exceptSet, &vtimeout);
  37.     if (iRet <= 0) return iRet;

  38.     if (FD_ISSET(s, &exceptSet)) return 1;
  39. */
  40.     int error;
  41.     int len = sizeof(int);
  42.     getsockopt(s, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
  43.     if(error != 0) {
  44.         fprintf(stderr, "check_sock_error = [%d] \n", error);
  45.     }

  46.     return error;
  47. }

  48. int check_sock_send(int s)
  49. {
  50.     fd_set sendSet;
  51.     struct timeval vtimeout;
  52.     
  53.     FD_ZERO(&sendSet);
  54.     FD_SET(s, &sendSet);
  55.     
  56.     vtimeout.tv_sec = 1;
  57.     vtimeout.tv_usec = 0;
  58.     
  59.     int iRet =SockSelect(s , NULL, &sendSet, NULL, &vtimeout);
  60.     if (iRet <= 0) return iRet;

  61.     if (FD_ISSET(s, &sendSet))
  62.         return 1;
  63. }

  64. int check_sock_recv(int s)
  65. {
  66.     fd_set sendSet;
  67.     struct timeval vtimeout;
  68.     
  69.     FD_ZERO(&sendSet);
  70.     FD_SET(s, &sendSet);
  71.     
  72.     vtimeout.tv_sec = 5;
  73.     vtimeout.tv_usec = 0;
  74.     
  75.     int iRet =SockSelect(s , &sendSet, NULL, NULL, &vtimeout);
  76.     if (iRet <= 0) return iRet;

  77.     if (FD_ISSET(s, &sendSet))
  78.         return 1;
  79. }

  80. int set_keepalive(int s)
  81. {
  82.     int keepAlive = 1;//设定KeepAlive
  83.     int keepIdle = 1;//开始首次KeepAlive探测前的TCP空闭时间
  84.     int keepInterval = 1;//两次KeepAlive探测间的时间间隔
  85.     int keepCount = 1;//判定断开前的KeepAlive探测次数    
  86.     int flag=1;
  87.     int len=sizeof(int);
  88.     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flag, len);            
  89.     struct timeval timeout={3,0};//3s            
  90.     setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));            
  91.     setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));    

  92.     if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1)
  1.     {
  2.         fprintf(stderr, " SO_KEEPALIVE failed .\n");
  3.     }

  4.     if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1)
  5.     {
  6.         fprintf(stderr, " TCP_KEEPIDLE failed .\n");
  7.     }

  8.     if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1)
  9.     {
  10.         fprintf(stderr, " TCP_KEEPINTVL failed .\n");
  11.     }

  12.     if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1)
  13.     {
  14.         fprintf(stderr, " TCP_KEEPCNT failed .\n");
  15.     }
  16. }

  17. void closesocket(int s)
  18. {
  19.     if (s != -1) {
  20.         shutdown(s, SHUT_RDWR);
  21.         close(s);
  22.     }
  23. }
  24. static void proc_sig_pipe(int signo)
  25. {
  26.     fprintf(stderr, "*** maybe Opposite Side socket closed. ***\n");
  27.     //closesocket(sockfd);
  28.     //sockfd = -1;
  29. }

  30. /*初始化信号句柄*/
  31. void regSigProcHandle(void)
  32. {
  33.     struct sigaction sigact;
  34.     sigset_t block_mask;

  35.     //屏蔽本App之外所有的信号
  36.     sigfillset(&block_mask);
  37.     sigdelset(&block_mask, SIGPIPE);
  38.     sigprocmask(SIG_BLOCK, &block_mask, NULL);

  39.     //信号处理
  40.     sigfillset(&sigact.sa_mask);
  41.     sigact.sa_handler=proc_sig_pipe;
  42.     sigaction(SIGPIPE, &sigact, NULL);
  43. }

  44. int main()
  45. {
  46.     struct sockaddr_in servaddr;
  47.     int ret;

  48.     /*register some signals */
  49.     regSigProcHandle();

  50.     while (1) {
  51.         if (sockfd == -1) {
  52. //1. 连接服务器
  53. //此处不应当使用阻塞式调用,因为当网络持续不通时,此处可能会变得无限长时间( <= 2 * MSL = (1-4分钟))的阻塞。
  54. //建议使用 sockfd 设置成非阻塞状态。
  55. //然后connect 调用完毕后,使用select检测sockfd的可发送状态,
  56. //若如select超时,则说明connect超时,
  57. //若成功,则把sockfd从新设置成阻塞状态,进行下一步操作。

  58.             fprintf(stderr, "------- reconnected.-------------- \n");
  59.             if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  60.             {
  61.                 fprintf(stderr, "socket create error. \n");
  62.                 sleep(1);
  63.                 continue;
  64.             }

  65.             //set non-blocking
  66.             int flags;
  67.             flags = fcntl(sockfd, F_GETFL); //if (flags<0) { error();}
  68.             flags |= O_NONBLOCK;
  69.             fcntl(sockfd, F_SETFL, flags);
  70.             /*
  71.             int nonblock = 1;
  72.             if (ioctl(sockfd, FIONBIO, &nonblock) < 0) {
  73.                 close(sockfd);
  74.                 perror("ioctl FIONBIO");
  75.             }
  76.             */
  77.             servaddr.sin_family = AF_INET;
  78.             servaddr.sin_addr.s_addr = inet_addr("192.168.10.147");
  79.             servaddr.sin_port = htons(5000);

  80.             if ((ret = connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) == -1) {
  81.                 if (errno == EINPROGRESS) {
  82.                     int error;
  83.                     int len = sizeof(int);
  84.                     fd_set set;
  85.                     struct timeval tv_timeout;
  86.                     tv_timeout.tv_sec = 10;
  87.                     tv_timeout.tv_usec = 0;
  88.                     FD_ZERO(&set);
  89.                     FD_SET(sockfd, &set);
  90.                     if(select(sockfd + 1, NULL, &set, NULL, &tv_timeout) > 0) {
  91.                         getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
  92.                         if(error != 0) {
  93.                             fprintf(stderr, "socket error. [%d] \n", error);
  94.                             closesocket(sockfd);
  95.                             sockfd = -1;
  96.                             sleep(1);
  97.                             continue;
  98.                         }
  99.                     } else { //timeout or select error
  100.                         fprintf(stderr, "connect timeout. \n");
  101.                         closesocket(sockfd);
  102.                         sockfd = -1;
  103.                         sleep(1);
  104.                         continue;
  105.                     }
  106.                 }
  107.                 else { //exception
  108.                     fprintf(stderr, "connect exception. \n");
  109.                     sockfd = -1;
  110.                     sleep(1);
  111.                     continue;
  112.                 }
  113.             }
  114.             else if (ret == 0) { // connect success.
  115.                 fprintf(stderr, "connect success. \n");
  116.             }

  117.             // reconfig to block mode
  118.             int nonblock = 0;
  119.             if (ioctl(sockfd, FIONBIO, &nonblock) < 0) {
  120.                 closesocket(sockfd);
  121.                 perror("ioctl FIONBIO");
  122.             }

  123.             set_keepalive(sockfd);
  124.         }
  125.         
  126. //2. 发送数据
  127.         //此处应当先判断当前连接是否有异常。
  128.         //1. 异常判断 getsockopt(s, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
  129.         //2. 规避远程对端的sock关闭,此处会跑出 SIGPIPE异常造成崩溃。
  130.         //3. 尽量改用select方式先进行超时处理。
  131. /*
  132. //error
  133.         //fprintf(stderr, "socket check error. \n");
  134.         //getc(stdin);
  135.         ret = check_sock_error(sockfd);
  136.         if (ret > 0) {
  137.                 fprintf(stderr, "socket has error. \n");
  138.                 closesocket(sockfd);
  139.                 sockfd = -1;
  140.                 sleep(1);
  141.                 continue;
  142.         }
  143.         else {
  144.             fprintf(stderr, "socket is OK.\n");
  145.         }
  146. //*/
  147. //send
  148.         //fprintf(stderr, "socket send check. \n");
  149.         //getc(stdin);
  150.         ret = check_sock_send(sockfd);
  151.         if (ret <= 0) {
  152.                 fprintf(stderr, "socket has error for send. \n");
  153.                 closesocket(sockfd);
  154.                 sockfd = -1;
  155.                 sleep(1);
  156.                 continue;
  157.         }
  158.         else {
  159.             fprintf(stderr, "socket no error for send. \n");
  160.         }

  161.         const char pSend[]="iibull";
  162.         char pRecv[256];

  163.         ret = send(sockfd, pSend, strlen(pSend), 0);
  164.         if (ret <= 0) {
  165.                 fprintf(stderr, "send error. [%d]\n", ret);
  166.                 closesocket(sockfd);
  167.                 sockfd = -1;
  168.                 sleep(1);
  169.                 continue;
  170.         }
  171.         else {
  172.             fprintf(stderr, "send success. [%d]\n", ret);
  173.         }

  174. //3. 收取数据
  175.         //1. 首先当检测socket上是否有错误。
  176.         //2. 尽量改用select方式先进行分段超时处理,以节省系统资源。
  177. /*
  178. //error
  179.         fprintf(stderr, "socket error check. \n");
  180.         getc(stdin);
  181.         ret = check_sock_error(sockfd);
  182.         if (ret > 0) {
  183.                 fprintf(stderr, "socket has error. \n");
  184.                 closesocket(sockfd);
  185.                 sockfd = -1;
  186.                 sleep(1);
  187.                 continue;
  188.         }
  189.         else {
  190.             fprintf(stderr, "socket no error for recv. \n");
  191.         }
  192. //*/
  193. //recv        
  194.         //fprintf(stderr, "socket recv check. \n");
  195.         //getc(stdin);
  196.         ret = check_sock_recv(sockfd);
  197.         if (ret <= 0) {
  198.                 fprintf(stderr, "socket has error for recv. \n");
  199.                 closesocket(sockfd);
  200.                 sockfd = -1;
  201.                 sleep(1);
  202.                 continue;
  203.         }
  204.         else {
  205.             fprintf(stderr, "socket is OK for recv. \n");
  206.         }

  207.         memset(pRecv, sizeof(pRecv), 0);
  208.         ret = recv(sockfd, pRecv, sizeof(pRecv)-1, 0);
  209.         if (ret <= 0) {
  210.                 fprintf(stderr, "recv error. [%d]\n", ret);
  211.                 closesocket(sockfd);
  212.                 sockfd = -1;
  213.                 sleep(1);
  214.                 continue;
  215.         }
  216.         else {
  217.             fprintf(stderr, "### recv OK [%s]###\n", pRecv);
  218.         }

  219.         sleep(1);
  220.     }
  221. }

参考:
http://blog.patpig.com/2012/06/c-socket-%E5%85%B3%E4%BA%8Econnect%E8%B6%85%E6%97%B6%E8%AE%BE%E7%BD%AE/
http://www.cnblogs.com/jason-jiang/archive/2006/11/03/549337.html


linux和windows下用setsockopt设置SO_SNDTIMEO,SO_RCVTIMEO的参数的一点区别

linux和windows下用setsockopt设置SO_SNDTIMEO,SO_RCVTIMEO的参数的一点区别  


UDP的socket在某些情况:如对方关闭时,本地可能sendto不出去数据,然后recvfrom就会被阻塞,这时就需要设置 这两个参数的值提高程序质量。

linux:

    struct timeval timeout={3,0};//3s
    int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));
    int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));

    如果ret==0 则为成功,-1为失败,这时可以查看errno来判断失败原因
    int recvd=recv(sock_fd,buf,1024,0);
    if(recvd==-1&&errno==EAGAIN)
   {
        printf("timeout\n");
   }


windows:
int timeout = 3000; //3s
   int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));

   int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));


而solaris,则不支持。


阅读(5239) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~