Windows上如何玩非阻塞的connect?---让程序员自定义connect函数的超时时间

本文介绍了如何在Windows下使用QT编程时,通过非阻塞的connect函数实现自定义超时时间,避免因服务器连接问题导致程序卡顿。通过设置socket为非阻塞模式,并结合select函数,可以在指定超时时间后判断连接是否成功,提高了用户体验。

文章转载自:https://blog.csdn.net/stpeace/article/details/43622943
最近接触Windows下关于QT的编程,由于在编程中需要访问某服务器获取数据,在项目调试期间,发现假如服务器连不上时(如果腐服务器没有运行,或者服务的IP地址和端口有误的话),程序会卡好一会儿才正常继续往下运行,故考虑是connect服务器的时候阻塞了,感谢所转载的这篇文章的作者,解决了我这个问题。

以下内容为转载*******************************************************:

我们知道, 对于阻塞的socket而言, connect函数也是阻塞的, 我在Windows上测试过, 对于阻塞的socket而言, connect的阻塞时间约为25s(linux上是75s吧, 各个平台都不一样). 也就是说, 很多时候, 客户端需要等25s才继续往下执行。 我们想象一下, 用户肯定会不满意啊, 得罪了用户, 那就糟糕了。 那能不能搞个自己设置超时时间的connect函数呢? 完全可以! 在本文中, 我们来学习一下非阻塞connect函数的实现—让程序员自定义connect函数的超时时间。

说明: 两年后, 当我再次审视这些程序的时候, 我发现, select后, 强烈建议做FD_ISSET检查。

直接上客户端的代码:

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
 
int main()
{
	// 网络初始化
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(2, 2);
	WSAStartup( wVersionRequested, &wsaData );
 
 
	// 创建客户端socket(默认为是阻塞socket)
	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
 
 
	// 设置为非阻塞的socket
	int iMode = 1;
	ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode); 
 
 
	// 定义服务端
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(8888);
 
 
	// 超时时间
	struct timeval tm;
	tm.tv_sec  = 5;
	tm.tv_usec = 0;
	int ret = -1;
	
 
	// 尝试去连接服务端
	if (-1 != connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
	{
		ret = 1; // 连接成功
	}
	else
	{
		fd_set set;
		FD_ZERO(&set);
		FD_SET(sockClient, &set);
 
		if (select(-1, NULL, &set, NULL, &tm) <= 0)
		{
			ret = -1; // 有错误(select错误或者超时)
		}
		else
		{
			int error = -1;
			int optLen = sizeof(int);
			getsockopt(sockClient, SOL_SOCKET, SO_ERROR, (char*)&error, &optLen); 
			
			// 之所以下面的程序不写成三目运算符的形式, 是为了更直观, 便于注释
			if (0 != error)
			{
				ret = -1; // 有错误
			}
			else
			{
				ret = 1;  // 无错误
			}
		}
	}
 
 
	// 设回为阻塞socket
	iMode = 0;
	ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode); //设置为阻塞模式
 
 
	// connect状态
	printf("ret is %d\n", ret);
 
 
	// 发送数据到服务端测试以下
	if(1 == ret)
	{
		send(sockClient, "hello world", strlen("hello world") + 1, 0);
	}
 
 
	// 释放网络连接
	closesocket(sockClient);
	WSACleanup();
 
	return 0;
}

我们先不管服务端, 直接运行上面的程序, 过5s后, 程序结果为:ret is -1

好, 我们关掉当面的客户端。 并启用下面的服务端:

#include <stdio.h>
#include <winsock2.h> // winsock接口
#pragma comment(lib, "ws2_32.lib") // winsock实现
 
int main()
{
	WORD wVersionRequested;  // 双字节,winsock库的版本
	WSADATA wsaData;         // winsock库版本的相关信息
	
	wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257
	
 
	// 加载winsock库并确定winsock版本,系统会把数据填入wsaData中
	WSAStartup( wVersionRequested, &wsaData );
	
 
	// AF_INET 表示采用TCP/IP协议族
	// SOCK_STREAM 表示采用TCP协议
	// 0是通常的默认情况
	unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
 
	SOCKADDR_IN addrSrv;
 
	addrSrv.sin_family = AF_INET; // TCP/IP协议族
	addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket对应的IP地址
	addrSrv.sin_port = htons(8888); // socket对应的端口
 
	// 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程)
	bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
 
	// 将socket设置为监听模式,5表示等待连接队列的最大长度
	listen(sockSrv, 5);
 
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);
 
	printf("To receive...\n");
	char recvBuf[100] = {0};
	recv(sockConn, recvBuf, 100, 0); // 接收客户端数据,最后一个参数一般设置为0
	printf("recv is %s\n", recvBuf);
 
	while(1);
 
	closesocket(sockConn);	
	closesocket(sockSrv);
	WSACleanup();
	
	return 0;
}

然后呢, 我们再启动客户端, 发现客户端立即出现:ret is 1, 服务端对应的结果为:

To receive…
recv is hello world

由此可见, 上面的客户端程序实现了非阻塞的connect, 也就是用, 程序员可以自定义超时时间。 ok, 先这样。

以上内容为转载*******************************************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值