I start a server on :: to accept all possible connections.
void StartServer(std::string ip, unsigned int port)
{
union sockaddr_types
{
struct sockaddr_storage storage;
struct sockaddr addr;
struct sockaddr_in in4;
struct sockaddr_in6 in6;
};
sockaddr_types address_storage = {};
if (inet_pton(PF_INET, ip.c_str(), &address_storage.in4.sin_addr) == 1)
{
address_storage.in4.sin_family = AF_INET;
address_storage.in4.sin_port = htons(port);
}
else if (inet_pton(PF_INET6, ip.c_str(), &address_storage.in6.sin6_addr) == 1)
{
address_storage.in6.sin6_family = AF_INET6;
address_storage.in6.sin6_port = htons(port);
}
else
{
return;
}
SocketFd = socket(address_storage.addr.sa_family, SOCK_STREAM, 0); // SocketFd a class member or a global variable
int opt = 1;
int ret = 0;
if (setsockopt(SocketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
{
// Error
}
else
{
if ((ret = bind(SocketFd, &address_storage.addr, sizeof(address_storage))) < 0)
{
// Error
}
else
{
if ((ret = listen(SocketFd, QueueSize)) < 0)
{
// Error
}
else
{
while (true) // Just to simplify example
{
int clientScoketFd = 0;
sockaddr_in clientAddr = {};
socklen_t clientAddrlen = sizeof(clientAddr);
if ((clientScoketFd = accept(SocketFd, (sockaddr *)&clientAddr, &clientAddrlen)) < 0)
{
// Error
}
char buff[255] = {};
inet_ntop(clientAddr.sin_family, &clientAddr.sin_addr, buff, sizeof(buff));
printf("IP %s\n", buff);
DoJobOnSocket(clientScoketFd, buff);
}
}
}
}
}
Once a client with IPv4 connects to the server the output is
"IP ::"
Why it is zero and not an IPv4? When I start on IPv4 address this works fine, I see client's IPv4. Is there a way to get the IPv4 here for this situation, or I have to start server on IPv4 address?
CodePudding user response:
The client probably connected to the server using IPv6 and not via IPv4. You should check the address family (sin_family
in your case). If that were AF_INET
, then the inet_ntop
would never return ::
.
Checking just the sin_family
member is relatively safe, but checking sin_addr
is not if the address might be an IPv6 address. You should use a struct sockaddr_storage
to store the address, but cast it as a struct sockaddr
and just use the sa_family
member until you know which type of address it is. Then you can cast it as struct sockaddr_in
or struct sockaddr_in6
.
The getnameinfo
function deals well with this. It does all of this automatically.