connect有默认的超时时间,非阻塞connect的实现依赖于select,select的一个参数可以用来指定超时时间。
非阻塞connect函数的实现,
1.调用fcntl将套接字设置为非阻塞
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
connect(sockfd, (SA*)&saddr, sizeof(saddr));
if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
{
socklen_t len = sizeof(error);
/*SO_ERROR 获取错误状态*/
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
{
close(sockfd);
return -1;
}
}
if (error)
{
close(sockfd);
return -1;
}
}
示例程序
/*
*connect_nowait 非阻塞connect建立连接并返回连接好的套接字
*addr 连接到的ipv4地址
*port 连接到的端口
*nsec 超时时间
*fp 函数指针
*返回值:成功返回套机字,出错返回-1
*/
int connect_nowait(const char* addr,const unsigned int port,
const int nsec, void (*fp)())
{
int sockfd;
struct sockaddr_in saddr;
int flags;
int r;
int error;
fd_set rset, wset;
struct timeval tval;
if (addr == NULL)
return -1;
/*地址格式不合法*/
if (inet_addr(addr) == -1)
return -1;
/*端口超出范围*/
if (port > 65535)
return -1;
/*时间不合法*/
if (nsec < 0)
return -1;
if (fp == NULL)
return -1;
bzero((void*)&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(addr);
saddr.sin_port = htons(port);
/*socket异常*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1)
{
close(sockfd);
return -1;
}
/*设置为非阻塞*/
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1)
{
close(sockfd);
return -1;
}
/*对非阻塞的TCP套接字调用connect,期待返回EINPROGRESS错误,表示引发三路握手,但连接尚未完成*/
if ((r = connect(sockfd, (SA*)&saddr, sizeof(saddr))) == -1)
{
if (errno != EINPROGRESS)
{
close(sockfd);
return -1;
}
}
/*建立连接期间,这里可以做一些其他的处理,提高时间利用率*/
fp();
/*如果客户-服务器都在一台主机上,那么这里可能会立即建立连接*/
if (r == 0)
goto done;
/*将套接字添加到读写描述符集中*/
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
wset = rset;
/*初始化超时时间*/
tval.tv_sec = nsec;
tval.tv_usec = 0;
/*select 异常*/
if ((r = select(sockfd+1, &rset, &wset, NULL, &tval)) == -1)
{
close(sockfd);
return -1;
}
/*select 超时*/
if (r == 0)
{
close(sockfd);
return -1;
}
/*
*三种情况
*1.套接字成功建立连接,此时可写
*2.连接出错,套接字既可读又可写
*3.成功建立连接,并有对端数据到达,套接字既可读又可写
*/
if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
{
socklen_t len = sizeof(error);
/*SO_ERROR 获取错误状态*/
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
{
close(sockfd);
return -1;
}
}
if (error)
{
close(sockfd);
return -1;
}
done:
if ((r = fcntl(sockfd, F_SETFL, flags)) == -1)
{
close(sockfd);
return -1;
}
return sockfd;
}#include <iostream>
#include "net.h"
using namespace std;
void while_connect()
{
for(int i=0; i<10; i++)
cout << "while_connect" << endl;
}
int main()
{
int sockfd;
char *addr = "182.92.78.165";
unsigned int port = 33333;
int nsec = 3;
sockfd = connect_nowait(addr, port, nsec, while_connect);
if (sockfd == -1)
cout << "连接失败" << endl;
cout << "连接成功" << endl;
return 0;
}执行结果:
总结:
非阻塞connect实际上与普通建立套接字并连接的步骤没有太大差别,只是调用fcntl将套接字设置为非阻塞后调用connect,同样引发三路握手,并不等待connect返回,,而去利用这段时间处理其他操作,更好的利用时间,之后再利用select检测套接字来判断连接是否完成,并且可以指定一个超时时间。
原文地址:http://blog.csdn.net/aspnet_lyc/article/details/29179501