Reputation:
I am create socket class but I want to make my Connect function dynamic and can connect to address(ipv4 or ipv6) use switch to make IPv test and connect to supported IPv just wan to ask if I am right or is there an easy way to make it to make IPv4 or IPv6?
bool Connect(short port,std::string addr,bool vlisten,HWND WindowHandle,WSADATA& wsaData,bool async)
{
if(!hSocket);
{
this->port = port;
this->addr =addr;
this->vlisten = vlisten;
this->WindowHandle = WindowHandle;
this->wsaData =wsaData;
this->init = true;
// Provide big enough buffer, ipv6 should be the biggest
char ipstr[INET6_ADDRSTRLEN];
char ipstr2[INET6_ADDRSTRLEN];
struct sockaddr_in* sockaddr_ipv4;
struct sockaddr_in6* sockaddr_ipv6;
//struct sockaddr_in6* sockaddr_ipv6;
if(WSAStartup(MAKEWORD(2,2),&wsaData) !=0)
{
throw runtime_error("Error WSAStartup:" + WSAGetLastError());
}
if((this->hSocket = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))== INVALID_SOCKET)
{
Close();
throw runtime_error("Error init sockect:" + WSAGetLastError());
}
if(addr != "INADDR_ANY")
{
struct addrinfo *result = nullptr;
getaddrinfo(addr.c_str(), nullptr, nullptr, &result);
struct addrinfo *it;
for (it = result; it != nullptr; it = it->ai_next)
{
//sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
//addr = inet_ntoa(sockaddr_ipv4->sin_addr);
//if (addr != "0.0.0.0") break;
switch (it->ai_family)
{
case AF_UNSPEC:
cout<<"Unspecified\n"<<endl;
break;
case AF_INET:
cout<<"AF_INET (IPv4)\n"<<endl;
sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
//printf("\tIPv4 address %s\n",
addr = inet_ntoa(sockaddr_ipv4->sin_addr);
/*if (addr != "0.0.0.0") break;*/
break;
case AF_INET6:
cout<<"AF_INET (IPv6)\n"<<endl;
sockaddr_ipv6 = reinterpret_cast<sockaddr_in6*>(it->ai_addr);
addr = inet_ntop(it->ai_family,sockaddr_ipv6,(PSTR)ipstr,sizeof(ipstr));
break;
case AF_NETBIOS:
cout<<"AF_NETBIOS (NetBIOS)\n"<<endl;
break;
default:
printf("Other %ld\n", it->ai_family);
break;
}
}
freeaddrinfo(result);
}
}
SOCKADDR_IN sockAddrIn;
memset(&sockAddrIn,0,sizeof(sockAddrIn));
sockAddrIn.sin_port = htons(port);
sockAddrIn.sin_family = AF_INET;
sockAddrIn.sin_addr.s_addr = (addr == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(addr.c_str()));
if(vlisten && (bind(hSocket,reinterpret_cast<SOCKADDR*>(&sockAddrIn),sizeof(sockAddrIn))== SOCKET_ERROR))
{
Close();
throw runtime_error("Error vlisten & bind: " + WSAGetLastError());
}
if(async && WindowHandle)
{
if(WSAAsyncSelect(hSocket,WindowHandle,WM_SOCKET,FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE|FD_ACCEPT) !=0)
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
}
if(vlisten && (listen(hSocket,SOMAXCONN)== SOCKET_ERROR))
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
if(!vlisten && (connect(hSocket, reinterpret_cast<SOCKADDR*>(&sockAddrIn), sizeof(sockAddrIn)) == SOCKET_ERROR))
{
if(async && WindowHandle && (WSAGetLastError() != WSAEWOULDBLOCK))
{
Close();
throw runtime_error("Error async & WindowHandle: " + WSAGetLastError());
}
}
}
Upvotes: 0
Views: 287
Reputation: 9980
Your code has multiple issues:
getaddrinfo()
, but then you completely threw away the results without using them.listen()
but you appear to intend to make an outgoing connection; listen()
is meant to listen for incoming connections.getaddrinfo()
you ignore it and assume IPv4 when filling out your sockaddr_in
structure. This part of the code should just be scrapped.Let's go back to the beginning and get a minimal outgoing connection up. I'm omitting anything here not directly related to creating the connection (e.g. the call to WSAAsyncSelect()
and other stuff which belongs in separate methods anyway):
// Connect to a remote host, given its address and port number.
bool Connect(short port, std::string addr)
{
struct addrinfo *result, *rp;
// TODO: You passed us an integer port number. We need a C string.
// Clean up this mess.
char portstr[255];
portstr = sprintf("%d", port);
if (getaddrinfo(addr.c_str(), portstr, nullptr, &result)) {
throw runtime_error("getaddrinfo: " + WSAGetLastError());
}
// A host can have multiple addresses. Try each of them in turn until
// one succeeds. Typically this will try the IPv6 address first, if
// one exists, then the IPv4 address. The OS controls this ordering
// and you should not attempt to alter it. (RFC 6724)
for (rp = result; rp != nullptr; rp = rp->ai_next) {
this->hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
// Check socket creation failed; maybe harmless (e.g. computer
// doesn't have IPv6 connectivity). Real errors will get thrown below.
if (this->hSocket == -1)
continue;
if (connect(this->hSocket, rp->ai_addr, rp->ai_addrlen) != -1)
break; // Success
close(this->hSocket);
}
if (rp == NULL) { // No address succeeded
throw runtime_error("connect: " + WSAGetLastError());
}
freeaddrinfo(result);
// Now this->hSocket has an open socket connection. Enjoy!
}
The major thing to note is that getaddrinfo()
handles all the heavy lifting for you. The structure it returns has all the information needed to create the connection; you only have to use it.
If you want the connection information, such as address and family, you can copy those out of rp
and store them somewhere before it goes out of scope.
Writing the separate method to handle incoming connections is left as an exercise for the reader. Your example code appears to be partly based on the sample on the MSDN page for getaddrinfo; the Linux getaddrinfo
manual page has much better examples (the sample code actually works with minimal change on Windows).
Upvotes: 1