创建能够超时的socket连结API

发表于:2007-07-04来源:作者:点击数: 标签:
在socket编程中调用connect函数连接一远程机器,如果该ip不存在的话,connect将在一段时间内返回-1表示失败。问题是有时候我们需要准确控制connect函数连接的时间,这就需要我们能够控制这个connect函数。但是,gcc给我们提供的API没有这个功能。我自己设计了
在socket编程中调用connect函数连接一远程机器,如果该ip不存在的话,connect将在一段时间内返回-1表示失败。问题是有时候我们需要准确控制connect函数连接的时间,这就需要我们能够控制这个connect函数。但是,gclearcase/" target="_blank" >cc给我们提供的API没有这个功能。我自己设计了一个这样的API函数--connect2,它能够在用户设置的一段时间后,连接超时退出。

      这个connect2的初步设计思想是利用多线程,创建一个独立的线程调用socket的connect函数去连接,这样这个线程就可以被杀掉。我们首先要根据用户的需求设立一个alarm,并设置好alarm到点之后的动作,那就是取消掉连接线程。如果在alarm到点之前这个线程已经返回那就返回连接成功,否则连接线程将被取消,也就说明连接在规定的时间内失败,函数返回失败。

      好了。设计思想比较简单。下面来看看实现。
//函数定义

int connect2(SOCKET sock,struct sockaddr * addr,int timeout)

//函数实现

/**This implementation is for LINUX
* @version 1.0
* @author Jerry Hou
*/
#include
#include
#include
#include
#include
#include
#include "connect_timeout.h"
int g_sock;
pthread_t connect_thread;
void *sock_connect(void * argv);
void sock_cancel(int no);
int connect2(SOCKET sock,struct sockaddr * addr,int timeout)
{
        int res;
        struct sigaction act;
        g_sock=sock;
        res=pthread_create(&connect_thread,NULL,sock_connect,(void *)addr);
        if(res!=0)
                return errno;
        act.sa_handler=sock_cancel;
        sigemptyset(&act.sa_mask);
        act.sa_flags=0;
        if(sigaction(SIGALRM,&act,NULL)<0)
        {
                return errno;
        }

        alarm(timeout);
        int * pres=NULL;
        res=pthread_join(connect_thread,(void **)&pres);
        alarm(0);
        if(res!=0)
                return errno;
        if(pres!=NULL)
        if(pres==PTHREAD_CANCELED)
                return -1;
        return *pres;
  

}
void * sock_connect(void * argv)
{
        struct sockaddr * s_addr=(struct sockaddr *)argv;
        int *pres=(int *)malloc(sizeof(int));
        int res=connect(g_sock,s_addr,sizeof(struct sockaddr_in));
        *pres=res;
        pthread_exit(pres);
}
void sock_cancel(int no)
{
        pthread_cancel(connect_thread);
//      timeout=1;
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这个connect2 API能够较准确在规定时间内TIMEOUT,但是还有一些问题。先不说了,改天有空再说。希望有人捧场。

     接着上面的讨论。细心的朋友会发现这个connect2函数的问题,那就是它只能允许在一个connect2调用完才能进行第二个调用,否则另外一个调用会影响到前面的调用。也就是说,只能在单线程中使用,不是线程安全(thread-safe)或可重入的(reentrant)。
     造成这个原因一是设计中我们调用了alarm这个gcc库的api,而它是进程范围的,也就是说后边alarm设置会影响到前面的设置。二是我们使用了2个全局变量,这样后边的connect2调用会改写这2个变量影响到前面的调用。
     解决的办法是我们需要一个线程安全的定时器timer,无论哪个定时器的设置都不会影响到其他的定时器工作。理想的这样一个timer函数应该是这样定义的:
int timer(int seconds,(void)ontimer(void * agrv) ,void *argv); 其中,seconds是超时时间,timerfunc是超时要执行的函数地址,argv是传入该函数的参数。好,我们假设有了这样一个线程安全的timer,我们来重写connect2。timer的实现我放在另外一篇文章里单独介绍,因为它不仅可以用在这里。

/**This implementation is for LINUX
* @version 1.1
* @author Jerry Hou
*/
#include
#include
#include
#include
#include
#include "timer.h"  //header file containing definitions of timer related APIs
#include "connect_timeout.h"

void * sock_connect(void * argv);
void  sock_cancel(void * argv);
int connect2(SOCKET sock,struct sockaddr * addr,int timeout)
{
        int res;
        pthread_t connect_thread;
        res=pthread_create(&connect_thread,NULL,sock_connect,(void *)addr);
        if(res!=0)
                return errno;
        res=timer(timeout,socke_cancel,(void *)&connect_thread);
        if(res!=0)
                return res;
        int * pres=NULL;
        res=pthread_join(connect_thread,(void **)&pres);
        if(res!=0)
                return errno;
        if(pres!=NULL)
        if(pres==PTHREAD_CANCELED)
                return -1;
        return *pres;
  

}
void * sock_connect(void * argv)
{
        struct sockaddr * s_addr=(struct sockaddr *)argv;
        int *pres=(int *)malloc(sizeof(int));
        int res=connect(g_sock,s_addr,sizeof(struct sockaddr_in));
        *pres=res;
        pthread_exit(pres);
}
void  sock_cancel(void * argv)
{
        pthead_t *pthread=(pthead_t *)argv;
        pthread_cancel(pthread);
//      timeout=1;
}

原文转自:http://www.ltesting.net