I am trying to create a socket using a hostname and port. However, ::select()
implies the socket has no data.
Can someone please help?
Self-contained example below (and Godbolt link).
https://godbolt.org/z/r7G3aczdK
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <string>
#include <iostream>
bool isSocketReady(int sockfd)
{
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
tv.tv_sec = 2;
tv.tv_usec = 0;
const int retval = ::select(sockfd, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
printf("select() error");
return false;
}
else if (retval)
{
printf("Data is available now.\n");
return true;
}
else
{
printf("No data within timeout.\n");
return false;
}
}
int connectByHostName(const std::string& host, int port)
{
int sockfd = -1;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
const std::string port_str = std::to_string(port);
struct addrinfo* res{nullptr};
const int r = getaddrinfo(host.c_str(), port_str.c_str(), &hints, &res);
if(r < 0)
{
return -1;
}
for (struct addrinfo* address = res; address != nullptr; address = address->ai_next)
{
sockfd = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
if (sockfd < 0)
{
continue;
}
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(flag));
if(::connect(sockfd, address->ai_addr, address->ai_addrlen) < 0)
{
continue;
}
else if(isSocketReady(sockfd))
{
break;
}
}
freeaddrinfo(res);
return sockfd;
}
int main()
{
int sock = connectByHostName("example.com", 80);
std::cout << sock << std::endl;
}
You are calling select()
incorrectly.
You need to change this:
FD_SET(0, &rfds);
to this:
FD_SET(sockfd, &rfds);
And change this:
::select(sockfd, ...);
to this:
::select(sockfd+1, ...); // or 0 on Windows
That being said, there is no point is using isSocketReady()
after connect()
in the code you have shown. Both HTTP and WebSocket protocols require you to send a request before you can then read any data, but you are not doing that, so the socket will never report as readable.
If you are trying to use isSocketReady()
to check if connect()
is successful, then this is wrong too, for 2 reasons:
you would have to test the socket for writability instead of readability.
you are not putting the socket into non-blocking mode (TCP_NODELAY
is not meant for that purpose), so connect()
will block the calling thread until the connection is fully established or fails. So you can't use select()
to check for that condition. To put the socket into non-blocking mode, you have to do one of the following (depending on which platform your code is running on):
SOCK_NONBLOCK
flag on the socket()
call.fctrl(F_SETFL)
to enable the O_NONBLOCK
flag on the socket.ioctl()
(posix) or ioctlsocket()
(Windows) to enable the FIONBIO
option on the socket.