這個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;
}