以下是我最终为异步客户端做的代码:
struct addrinfo *currentAddress = NULL;
void GetAddresses(std::string hostname, int port)
{
struct addrinfo hints;
std::cout << "Get adresses for hostname " << hostname << " port " << port << std::endl;
std::memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPV4 or IPV6 */
hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
std::string portStr;
portStr = std::to_string(port);
int status = getaddrinfo(hostname.c_str(), portStr.c_str(), &hints, ¤tAddress);
if (status != 0)
{
std::stringstream ss;
ss << "Cannot resolve hostname " << hostname << ". Error: " << gai_strerror(status);
throw std::runtime_error(ss.str());
}
}
void StepNextAddress()
{
currentAddress = currentAddress->ai_next;
}
void Connect(const std::string& address, int port)
{
/*
* Get all addresses for connection
*/
GetAddresses(address, port);
bool success = false;
while (currentAddress != NULL)
{
int s = socket(currentAddress->ai_family, currentAddress->ai_socktype, currentAddress->ai_protocol);
if (s == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error creating socket. Out of resources.";
throw std::runtime_error(ss.str());
}
std::cout << "Socket created: " << s << std::endl;
#ifdef _WIN32
unsigned long on = 1;
ioctlsocket(s, FIONBIO, &on); // Make it non blocking
#else
fcntl(s, F_SETFL, O_NONBLOCK); Make if non blocking
#endif
/*
* Connect to socket
*/
int status = connect(s, currentAddress->ai_addr, currentAddress->ai_addrlen);
if (status < 0 && errno != EINPROGRESS)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error connecting socket to " << address << " port " << port << ".";
throw std::runtime_error(ss.str());
}
/*
* Wait socket to get ready to select
*/
fd_set readSet, writeSet, exceptSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&exceptSet);
timeval tout;
tout.tv_sec = CONNECT_TIMEOUT_SECONDS;
int retval = select (s + 1, &readSet, &writeSet, &exceptSet, &tout);
if (retval == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error selecting socket.";
throw std::runtime_error(ss.str());
}
/*
* Timeout. Try next resources
*/
if (retval == 0)
{
std::cout << "Connection timedout. Trying next resource." << std::endl;
#ifdef _WIN32
_close(s);
#else
close(s);
#endif
StepNextAddress();
continue;
}
/*
* Wait socket to be ready to work
*/
int result = 0;
socklen_t result_len = sizeof(result);
int sts = getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &result, &result_len);
if (sts < 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket option.";
throw std::runtime_error(ss.str());
}
if (result != 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket resources.";
throw std::runtime_error(ss.str());
}
}
freeaddrinfo(currentAddress);
std::cout << "CONNECTED!!!!!!!!!!!!" << std::endl;
}
int main()
{
Connect("test.test.com", 21);
}
我还不了解select
与getsockopt
的使用情况。它们是必要的还是在我的情况下select
就足够了?
在这种情况下,select
正在发生错误......选择代码出了什么问题?
另外,这里的错误处理怎么样...我尝试连接列表中的下一个地址的唯一情况是超时...这有意义吗?
要完成,我们非常感谢代码中的评论。