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