ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have a socket library that I tinker with from time to time. Lately, I decided to expand it to support IPv6.
One of the issues I am having is sending data, using sendto(). When the server receives data from a client, using recvfrom(), it does not retain the sockaddr_in information, but instead provides the callee the IPv6 address and port number from where the data originated. See here:
Code:
int
UDPSocket::recv(char* buffer, size_t bufferLen, std::string& sourceAddr, unsigned short& sourcePort)
{
sockaddr_in sin;
socklen_t sinLen = sizeof(sin);
int bytesRcvd = ::recvfrom(m_Socket, buffer, bufferLen, 0, (sockaddr*) &sin, (socklen_t*) &sinLen);
if (bytesRcvd == -1)
{
throw SocketException("Error occurred when attempting to receive data", errno);
}
char buf[INET6_ADDRSTRLEN + 1] = {0};
const char* res = inet_ntop(m_Domain, &sin.sin_addr, buf, sizeof(buf));
sourceAddr = (res ? res : "Source Not Available");
sourcePort = ntohs(sin.sin_port);
return bytesRcvd;
}
As indicated above, after the UDPSocket::recv() method is called, the callee is returned a string and a port number. When UDPSocket::send() is called, the string/number pair is used to formulate the destination address using getaddrinfo(). However that fails. Why is this?
Here's the send() method:
Code:
void
UDPSocket::send(const char* msg, size_t msgLen, const std::string& destAddr, unsigned short destPort)
{
struct addrinfo info;
fillAddress(info, destPort, destAddr.c_str());
if (::sendto(m_Socket, msg, msgLen, 0, info.ai_addr, info.ai_addrlen) != (int) msgLen) // <-- FAILS HERE
{
throw SocketException("Error occurred when attempting to send data", errno);
}
}
I believe you copy a member struct sockaddr* with memcpy (note a pointer, not an object) and then free it indirectly with freeaddrinfo. Maybe you should pass a struct sockaddr& to this function and copy what's pointed to by host_info->ai_addr instead of passing struct addrinfo&.
Kevin Barry
PS You might comment out the freeaddrinfo line temporarily, before making major changes, to see if this is actually the problem.
I believe you copy a member struct sockaddr* with memcpy (note a pointer, not an object) and then free it indirectly with freeaddrinfo. Maybe you should pass a struct sockaddr& to this function and copy what's pointed to by host_info->ai_addr instead of passing struct addrinfo&.
Kevin Barry
PS You might comment out the freeaddrinfo line temporarily, before making major changes, to see if this is actually the problem.
Kevin, thanks for the reply. I tried both of your suggestions, however I went from the same predicament to a worse situation. Merely commenting out freeaddrinfo() didn't buy me anything (ie results were the same). When I changed the code to rely on a struct sockaddr versus using a struct addrinfo, then things turned for the worse. I could not even get the server code to bind() the socket. An invalid argument (errno = 22) was returned.
With the original code that I submitted earlier, I was able to get the server to bind() the socket, and receive a message from a client (that also uses the same socket library).
Here's the revamped fillAddress() function, which succeeds. It is when I bind() that the server fails.
I'm accustomed to working with struct sockaddr_in for my IPv4 connection information, and I have seen that there's a struct sockaddr_in6; how do these structures relate to struct sockaddr? Are the structures the same size? I wonder if I am losing context of the data when I copy the host_info->ai_addr using a sizeof(sockaddr)?
Last edited by dwhitney67; 07-25-2011 at 08:30 PM.
So now, I am able to get the Server running once again. The Client is able to send data to the Server, but when the Server replies, I get the following error:
Code:
Error occurred when attempting to send data [ Network is unreachable (101) ]
So it seems that I'm back to square one... albeit with better code.
I'm accustomed to working with struct sockaddr_in for my IPv4 connection information, and I have seen that there's a struct sockaddr_in6; how do these structures relate to struct sockaddr? Are the structures the same size? I wonder if I am losing context of the data when I copy the host_info->ai_addr using a sizeof(sockaddr)?
struct sockaddr is just a generalization of all socket-address formats for the purposes of passing them as arguments. It's basically a polymorphism hack so different socket types can be used with the same functions, hence the specification of family and address length in most functions. Given that IPv4 addresses are shorter than both IPv6 and Unix-socket addresses, struct sockaddr is probably the same size as struct sockaddr_in. Therefore:
struct sockaddr_in6 is almost certainly not the same size as struct sockaddr_in; use each of them only for their respective protocols.
Note, Client sends data successfully to host at "::1" via port 9000; Server fails in its attempt to send data back to the Client at "::0.60.177.163" via port 51686.
Last edited by dwhitney67; 07-25-2011 at 10:53 PM.
I have seen that there's a struct sockaddr_in6; how do these structures relate to struct sockaddr?
They are compatible; they can be substituted for each other. All these structures begin with the type identifier (a short int), which identifies the exact type of the socket. The length varies.
Quote:
Originally Posted by dwhitney67
Are the structures the same size?
No, and struct sockaddr is not large enough (16 bytes, I think) to hold a struct sockaddr_in6 (28 bytes, I think).
Nothing stops you from defining such a "supertype" yourself, however:
union sockaddr_any address;
/* As struct sockaddr * &(address.any)
* As struct sockaddr_in * &(address.in)
* As struct sockaddr_in6 * &(address.in6)
*/
The above address variable can specify either socket type, and you can add more socket types simply by extending the union. This works, because all struct sockaddr types are compatible, and union sockaddr type is guaranteed to be at least as large as the largest member type.
In your case, you could simply use the union sockaddr_any type everywhere.
Last edited by Nominal Animal; 07-25-2011 at 11:56 PM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.