tcp socket 客户端重连机制

在Linux下编程使用socket 客户端连接服务器时发现一个问题

有网友测试



(1)客户端已经连接,当服务端关闭程序时,客户端调用send函数发送失败,WSAGetLastError() 返回10054(远程主机强迫关闭了一个现有的连接) 

(2)客户端已经连接,当客户端关闭程序时,服务端调用recv函数接收失败,WSAGetLastError() 返回10054(远程主机强迫关闭了一个现有的连接) ,这时对这个客户端的socket调用select函数时,会立即返回可读。

(3)客户端已经连接,当服务端关闭程序时,客户端调用recv函数接收失败,WSAGetLastError() 返回10054

(4)WSAAsyncSelect模式的服务端,当客户端软件关闭时,服务器收到的消息为FD_CLOSE,错误代码为10053(您的主机中的软件中止了一个已建立的连接)


本人发现的一个现象是

当tcp socket 和服务器程序已经建立连接而服务器在关闭socket,而客户端在未知其关闭的情况下,如果继续向socket发送数据,那么将会导致程序崩溃或异常退出,这个时候所以加上心跳判断还是很有必要的,如果心跳超时,将不在socket里边发数据,而是建立重连机制。

以下是是实现代码

代码大概原理就是,重连的一个进程,socket使用的是阻塞模式这样在畅通的情况下,是会一直阻塞在接收receive 的函数里,当receive发生异常时,程序会直接跳到while(1)下,如果服务器还没连接,则会goto  重新来,直到连接成功,重新连接的时间是5s。

void *socket_tcp_handle (void *arg)

{
s32 socketfd_client = 0 ;

socklen_t addr_len = 0 ;
s32 num_bytes = 0 ;
struct timeval tm;
fd_set rfds;
int ret = 0 ;
int error=-1;
int len;
    u8 i;
char recvbuffer[250];

app_task_attr *taskattr = NULL ;
u8 msg_buf[MAX_MSG_BUFSIZE] = { 0 } ;


struct sockaddr_in remoteSockAddr;
DEBUG(LOG_DEBUG,"socket tcp bind 3333333333\n");

if (0 == arg)
{
DEBUG(LOG_ERR,"arg error!\n");
return NULL;
}

taskattr = (app_task_attr *) arg;
socketfd_local = taskattr->msg_id;



remoteSockAddr.sin_family = AF_INET;  
remoteSockAddr.sin_port = htons(cloud_port);  
//inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络   
//字节序整数表示的IPv4地址   
remoteSockAddr.sin_addr.s_addr = inet_addr(cloud_ip);   



addr_len = sizeof (remoteSockAddr);


DEBUG(LOG_DEBUG,"socket tcp bind socket success\n");
while(1){
loop:
DEBUG(LOG_DEBUG,"socket tcp   process loop \n");
if((heartbeat_cnt!=0))
{    
    DEBUG(LOG_DEBUG,"socket tcp   repeat connect \n");
connect_flg =0;
close (socketfd_local);
sleep(5);
if ((socketfd_local = socket (AF_INET, SOCK_STREAM, 0)) == -1){
DEBUG(LOG_ERR,"build socket error!\n");
return (-1);
}
int opts = fcntl(socketfd_local,F_GETFL);
if (opts < 0 ){
DEBUG(LOG_ERR,"fcntl get fl failed\n" );
return (-1);
}

remoteSockAddr.sin_family = AF_INET;  
remoteSockAddr.sin_port = htons(cloud_port);  
//inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络
//字节序整数表示的IPv4地址  
remoteSockAddr.sin_addr.s_addr = inet_addr(cloud_ip);



addr_len = sizeof (remoteSockAddr);


}
int connect_fd = connect(socketfd_local, (struct sockaddr*)&remoteSockAddr, addr_len);
if(connect_fd < 0)  
{
      if (errno != EINPROGRESS)
      {
          perror("connect");
DEBUG(LOG_DEBUG,"connect 1!\n");
goto loop;
               break;
      }
  
  perror("connect");
           DEBUG(LOG_DEBUG,"connect 2!\n");
}
 
if(connect_fd ==0 )
{
         //     goto done;
        DEBUG(LOG_DEBUG,"connect ok!\n");
system("aplay /home/user/yhfwqlj.wav");
connect_flg = 1;
//    DEBUG(LOG_ERR,"connect error!\n");
#if 0
// return 2;
tm.tv_sec  = 2;
tm.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(socketfd_local,&rfds);
ret = select(1+socketfd_local,NULL,&rfds,NULL,&tm);
switch(ret)  
                {  
                        case -1:  
                                perror("select");
close(socketfd_local); 
connect_flg =0;
                                return 1;  
                        case 0:  
close(socketfd_local); 
                                 DEBUG(LOG_ERR,"timeout\n");  
                                break;  
                        default:  
                                if(FD_ISSET(socketfd_local,&rfds))  
                                {  
                                        if(getsockopt(socketfd_local,SOL_SOCKET,SO_ERROR,&error,(socklen_t *)&len)<0)  
                                        {  
                                                return 1;  
                                    }  
                                        printf("error=%d\n",error);//error ==0 时 连接成功  
                                        if(error==0)  
                                        {  
                                                DEBUG(LOG_DEBUG,"connect ok!\n");
                                                ret=1;  
                                        }  
                                        else  
                                        {  
                                                ret=0;  
                                                errno=error;  
                                        }  
                                }  
                                break;  
                }  
    fcntl(socketfd_local,F_SETFL,fcntl(socketfd_local,F_GETFL) & O_NONBLOCK); //set back to block mode  
          
/*  if(!ret)  
        {  
                connect_flg =0;
                close(socketfd_local);  

                perror("connect failed");  
                return 1;  
        }
        */
#endif  
}
char buf[1024];  

// unsigned long ul = 1;
    // ioctl( socketfd_local, FIONBIO, &ul );  //设置为非阻塞模式
/*user task init func */

if(connect_flg ==1 )
{
if(taskattr->task_init != NULL){
(taskattr->task_init) (NULL);
}




 
   //    DEBUG(LOG_DEBUG,"socket_tcp client_handle   loop\n");
tm.tv_sec  = 1;
    tm.tv_usec = 0;
select (0, NULL, NULL, NULL, &tm);
        recv(socketfd_local,recvbuffer,250,0);//如果设置成阻塞模式,当recv的返回值为0时则代表服务器关闭
// for(i=0;i<46;i++)
     //   printf("%x ",recvbuffer[i]);
// printf(" ------------\n");
        if(recvbuffer[0]==0x68)
        {

if(taskattr->task_handle!= NULL){
(taskattr->task_handle) (recvbuffer, 250);;
}



}
      
   
}   
      


}

close (socketfd_local);
socketfd_local = 0;
return NULL ;
}


sitemap