可以通过两种编程方式来修改默认连接超时等待时间
一、通过setsockopt来设置套接字SO_SNDTIMEO属性,部分代码如下:
timeout.tv_sec = 20;
timeout.tv_usec = 0;
ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);
if (-1 == ret)
{
printf("[FILE:%s] [FUNC:%s] [Line:%d] \n", __FILE__, __func__, __LINE__ );
return -1;
}
ret = connect(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
if (-1 == ret)
{
if (errno == EINPROGRESS)
{
printf("[FILE:%s] [FUNC:%s] [Line:%d] connect timeout \n", __FILE__, __func__, __LINE__ );
}
return ret;
}
使用上述代码测试后,针对非子网不可访问目标主机进行通信,就不会等待默认2分钟之久了,超时20S就会返回。
该方式对于发送数据接口一样适用,不过对于超时返回的情况下,errno返回值会不一样,像connect则是置为EINPROGRESS,发送操作则是置为EAGIN或EWOULDBLOCK。
二、将套接字设置成非阻塞,由select接口来设置超时等待时间,部分代码如下:
block_flag = 1;
if (ioctl(sockfd, FIONBIO, &block_flag) != 0)
{
printf("[FILE:%s] [FUNC:%s] [Line:%d] ioctl failed. \n", __FILE__, __func__, __LINE__ );
return -1;
}
ret = connect(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
if (-1 == ret)
{
if (errno == EINPROGRESS)
{
FD_ZERO(&set);
FD_SET(sockfd, &set);
timeout.tv_sec = 20;
timeout.tv_usec = 0;
if (select(sockfd + 1, NULL, &set, NULL, &timeout) > 0)
{
(void)getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &err, &len);
if (0 == err)
{
ret = 0;
}
}
else
{
printf("[FILE:%s] [FUNC:%s] [Line:%d] timeout \n", __FILE__, __func__, __LINE__ );
}
}
}
block_flag = 0;
if (ioctl(sockfd, FIONBIO, &block_flag) != 0)
{
printf("[FILE:%s] [FUNC:%s] [Line:%d] \n", __FILE__, __func__, __LINE__ );
}
connect接口会立即返回EINPROGRESS错误,不过底层会继续进行TCP三次握手连接,应用需要使用select接口来判断连接成功还是失败,同样可以通过select的第三个参数来设置连接超时等待时间。
这里说到非阻塞,有一点需要理解,就是原本套接字是阻塞的,如果connect阻塞住了,CPU就会挂起该线程,只能去处理其他线程;如果设置成非阻塞,但其后select只是处理这一路连接,则仍旧是阻塞等待连接成功或失败为止,和前面的唯一区别是可设置等待时间。
一般设置非阻塞主要用于下面三种场景:
1、设置连接超时,就如前面所说的
2、多路复用,比如要建立多个连接,不用等这一路连接成功,才能建立下一个连接,这里注意是一个线程建立多个连接
3、在连接过程中处理其他事务
设置超时操作还好,这里的主要问题是如何判断连接是否建立成功了呢?这个通过查看《UNIX网络编程》了解,需要考虑两种情况,
1、目标主机是本机地址,则可能connect后已经建立连接,这种情况可以直接返回建立连接后的套接字
2、通过select来判断是否连接建立成功,如果连接建立成功,则套接字可写,不过书中有说道如果发生错误,则套接字可读可写,所以不能单纯的通过可写来判断,那么只能通过判断是否有发生错误来判断连接是否建立成功,如代码中通过getsockopt接口来获取错误信息。
需要注意连接成功后,需要将套接字重新设置为阻塞模式。前面讲的第一种方式其实也需要有类似的操作,否则后面的发送操作也会沿用开始设定的超时机制,除非明确就是要这样的。
1、
编码过程中一开始主机地址和端口使用的变量名分别是server和port,不过看man手册示例代码的时候,基本是命名为host和server,可见对于server我的理解是有偏差的。
想想一个功能一般称为服务,比如FTP服务,对应的端口就是服务端口,就如linux下的服务端口文件/etc/services。
2、
connect接口如果返回出错,想要重新连接,需要使用新的套接字,不能使用原有的套接字继续连接。