3

我有一个应用程序需要执行以下操作:

  • 如果发生事件(与服务器断开连接),则会启动一个长计时器(例如 5 分钟)。然后应用程序尝试重新连接到服务器。
  • 如果重新连接失败,则会启动一个短计时器(20 秒),该计时器应尝试再次重新连接。
  • 如果成功,则长计时器应继续运行。
  • 当长定时器到期时,如果没有连接,应用程序应该退出,否则它应该正常继续。

我受到限制,因为我不能使用线程,只能使用进程。我也不能等待 reconnect() 的结果返回。

到目前为止,我有这样的设计:

int main(int argc, char **argv)
{
    /* do main loop work, if disconnected, call reconnect() & continue doing work */
}

void reconnect()
{
    pid = fork();

    if (pid >= 0) {
        /*Successful fork*/
        if (pid == 0) {

            rv = attempt_reconnect;               
            if (rv == 0) {
                /*Notify sig_child Success*/
                exit(0);
            } else {
                /*Notify sig_child Fail*/
                exit(1);
            }
        }
    } 
}

void sig_child(int signum)
{
    if(fork returned success) {
        set flag to continue network stuff
    }
    else {
        alarm(20);
    }
}

void sig_alarm(int signo)
{
    /*Received alarm, trying to reconnect...*/
    reconnect();
}

任何帮助将不胜感激!

编辑

我想我有一个从这里的例子工作的解决方案。它允许我创建具有单独 ID 的计时器,然后确定哪个已向程序发出信号

4

4 回答 4

2

使用我在这里找到的以下代码,我已经实现了(我认为)我想要做的事情。

它允许分别使用函数“makeTtimer”和“timerHandler”创建和处理多个timer_t对象:

timer_t reconnect_timer_id;
timer_t timeout_timer_id;

int main(int argc, char **argv)
{
    /* do main loop work */
    if(disconnected()) {
        /*IF TIMEOUT SET, SKIP, IF OK, RESET*/
        if(timeout_set != 1) {
            "Schedule Alarm"
            makeTimer("Timeout Timer", &timeout_timer_id, 600,0);
            timeout_set = 1;
        } else {
              "Timeout alarm already set..";
        }
        reconnect();
    }
}

void reconnect()
{
    pid = fork();

    if (pid >= 0) {
        /*Successful fork*/
        if (pid == 0) {

            rv = attempt_reconnect;               
            if (rv == 0) {
                /*Notify sig_child Success*/
                exit(0);
            } else {
                /*Notify sig_child Fail*/
                exit(1);
            }
        }
    } 
}

void sig_child(int signum)
{
    if(fork returned success) {
        set flag to continue network stuff
    }
    else {
        "Reconnect fail, retrying in 20 seconds...";
        makeTimer("Reconnect Timer", &reconnect_timer_id, 20,0);
    }
}

static void
timerHandler( int sig, siginfo_t *si, void *uc )
{
    timer_t *tidp;
    tidp = si->si_value.sival_ptr;
    if ( *tidp == timeout_timer_id ) {

        if(state != STATE_CONNECTED) {
            "Timeout alarm received, not connected to server, exiting..."
            exit(0);
        } else {
            "Timeout alarm received, connected to server, continuing..."
            timeout_set = 0;
        }

    } else if ( *tidp == reconnect_timer_id ) {
        "Reconnect alarm received, retrying...";
        reconnect();
    }
}

static int
makeTimer( char *name, timer_t *timerID, int expireSeconds, int intervalSeconds )
{
    struct sigevent         te;
    struct itimerspec       its;
    struct sigaction        sa;
    int sigNo = SIGRTMIN;

    /* Set up signal handler. */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timerHandler;
    sigemptyset(&sa.sa_mask);

    if (sigaction(sigNo, &sa, NULL) == -1) {
          "Failed to setup signal handling for" *name;

        return(-1);
    }

    /* Set and enable alarm */
    te.sigev_notify = SIGEV_SIGNAL;
    te.sigev_signo = sigNo;
    te.sigev_value.sival_ptr = timerID;
    timer_create(CLOCK_REALTIME, &te, timerID);
    its.it_interval.tv_sec = intervalSeconds;
    its.it_interval.tv_nsec =0;

    its.it_value.tv_sec = expireSeconds;
    its.it_value.tv_nsec = 0;
    timer_settime(*timerID, 0, &its, NULL);
    return(0);
}
于 2013-09-17T14:44:01.500 回答
1

您只能通过在主循环中轮询警报和标志来执行此操作。在主循环中选择断开事件后,您可以设置 20 秒的警报并使用标志在处理程序或主循环中处理重新连接。继续设置 20 秒的警报,直到您重新连接或 5 五分钟到期。如果 20 秒警报发生 15 次,您可以在警报处理程序中再次检查。现在,如果 5 分钟到期,那么您可以重置警报(0)并通知主循环以关闭。像这样的东西。

int disconnect_event = 0;
int reconnect_try = 1;
int five_minutes  = 0;
int shutdown = 0;
void alarm_handler(int signum)
{
  if(five_minutes == 15)
  {
    alarm(0);
    shutdown = 1;
  }
  else
  {
    reconnect_try = 1;
    alarm(20);
    five_minutes++
  }

} 

int main(int argc, char*argv[])
{
  signal(SIGALRM, alarm_handler);
  while(1)
  {
    if(disconnect_event && reconnect_try)
    {
      if(reconnect() == 0)
      {
        alarm(20);
        reconnect_try = 0;
      }
      else
      {
         disconnect_event = 0;
         reconnect_try = 1;
      }
    }
    if(shutdown)
    {
      disconnect_event = 0;
      reconnect_try = 0;
      alarm(0)
    }

  }

}
于 2013-09-17T14:13:50.620 回答
1

一个想法可能是不使用alarm( ),而是创建计时器作为子kill进程,主进程在之后使用两个不同的信号sleep()

void timer( int nsec, int signum )
{
    pid_t pid = getpid();
    if( fork() > 0 ) {
        sleep( nsec );
        kill( pid, signum );
        exit( 0 );
    }
}

通过使用两个不同的信号(例如 SIGUSR1 用于短定时器,SIGUSR2 用于长定时器),您可以拥有这样的信号处理程序:

void sig_timer( int signum )
{
    if( signum == SIGUSR1 ) {
        // reconnect
    } else if( signumm == SIGUSR2 ) {
        if( !reconnected )
            exit( 11 );
    } 
}
于 2013-09-17T12:57:13.920 回答
0

从问题中不清楚应用程序是否需要继续运行 (1) 在 connect() 正在进行时,以及 (2) 在它处于“短暂等待”时。

如果它不需要这两个周期中运行,那么当你需要使用长定时器时,将长定时器到期的绝对时间放入某个变量中,如下所示:

time_t run_until = time(NULL) + 60;

并且在每次不成功的 connect() 之后检查当前时间是否大于 run_until:

if (connect_rc) {if (time(NULL) > run_until) shutdown(); else reconnect();}

您将获得上面描述的逻辑,而无需任何进程或线程。

如果您需要您的进程在 connect() 正在进行时继续运行,和/或在重新连接之前处于短暂暂停状态时,您可以使用非阻塞套接字。如何做到这一点是一个冗长的讲座;本质上,您将主循环安排在 select() 或 poll() 周围。这些函数让您等待一组事件中的一个发生,这些事件是:后台 connect() 的完成,或为 recv()/read() 准备好到达的数据,或者更重要的是,计时器到期。这种方法也不会涉及线程或进程。

于 2013-09-17T12:03:38.870 回答