UDP server connecting and sending data weirdness - c++

I am making async (well non-blocking rly) sockets lib for educational purposes. TCP part works just fine, but when it comes to UDP i experience weird behavior. Following code works as expected - server receives data:
MyUDPSocket server;
server.Bind(5551);
MyUDPSocket client;
client.Connect("192.168.0.103", 5551);
Sleep(10);
client.Write("\x0", 1);
Sleep(10);
client.Write("test", 5);
But if either of Sleep() or client.Write("\x0", 1); are commented out - it stops working. Server just would not get data. Here are some parts of my library to give you clue how exactly sockets are made:
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
...............................................
memset( &name, 0, sizeof(name) );
name.sin_family = AF_INET;
name.sin_port = htons( port );
hostent* hostinfo = gethostbyname( address );
name.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
connect(s, (sockaddr*)&name, sizeof name)
Nothing fancy as you see. Maybe it is some unspoken rule that sending one byte of data to initialize connection is required or something? I am really confused here.
Edit:
Write function as requested. name variable is very same that is set in Connect call whose code is above.
virtual int Write( void* data, int size )
{
return sendto(s, (const char*)data, size, 0, (sockaddr*)&name, sizeof name);
}
Edit:
Also in select() loop i check only for sockets being readable. Could it be case that socket is not writable due to connection being initialized? If that is the case it should solve First sleep. But what about sending one byte then?

Related

Can a UDP multicast socket be configured so that write() can be called rather than sendto()?

I am writing a C++ multicasting application on Linux Ubuntu.
In my C++ multicast sender class I do this:
uint16_t port = 5678;
const char* group = "239.128.128.128";
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(group);
addr.sin_port = htons(port);
const char* buf = "Hi there";
size_t bytes_to_write = 8;
size_t bytes_sent = sendto(fd, buf, bytes_to_write, 0, (struct sockaddr*) &addr, sizeof(addr));
Is there any way to configure the file descriptor so that I can call write() rather than sendto()? I would have thought there would be a setsockopt option or similar to do this?
Yes.
Per the documentation man 7 udp
When
connect(2) is called on the socket, the default destination address
is set and datagrams can now be sent using send(2) or write(2)
without specifying a destination address.
and, for generality, the POSIX spec for connect says
If the initiating socket is not connection-mode, then connect() shall set the socket's peer address, and no connection is made. For SOCK_DGRAM sockets, the peer address identifies where all datagrams are sent on subsequent send() functions, and limits the remote sender for subsequent recv() functions.
It's always worth checking the documentation for these, things, it isn't that impenetrable. FWIW I couldn't remember immediately whether you need connect() or bind() for this, and it took me a few seconds to find out.

recvfrom() not getting everything

I'm having some issues with recvfrom() not getting all the packets in C++. (and then blocking and not returning)
I send 1 query packet, and then a response is sent.
It's broken into multiple packets of 805 bytes and then ended with a packet of ~200 bytes.
From my tests, 54 packets are received in total.
However, my program is only receiving 25-35 packets total, and not the ending packet, although the packets seem to be arriving fine in WireShark.
My code can be seen here:
sockaddr_in local, dest;
local.sin_family = AF_INET;
local.sin_port = htons(58770);
local.sin_addr.S_un.S_addr = INADDR_ANY;
dest.sin_family = AF_INET;
dest.sin_addr.S_un.S_addr = inet_addr(QUERYADDR);
dest.sin_port = htons(20810);
SOCKET s;
if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
errex("socket() failed");
if(bind(s, (sockaddr*)&local, sizeof(local)) == -1)
errex("bind() failed!");
sendto(s, (const char*)QUERY, sizeof(QUERY), 0, (sockaddr*)&dest, sizeof(dest));
while(true)
{
sockaddr_in tsaddr;
char buf[8192];
int slcl = sizeof(tsaddr);
int res = recvfrom(s, buf, sizeof(buf), 0, (sockaddr*)&tsaddr, &slcl);
printf("%i\n", res);
}
closesocket(s);
WSACleanup();
Can anyone see anything wrong?
The most likely explanation is that the packets are being dropped somewhere along the line. Wireshark just tells you that the packets are on the wire, not that they are being correctly received by the receiver.
Try checking your SO_RCVBUF socket option on the receiver to make sure that's not being overrun (which will result in dropped packets), as well as checking your network driver for any indications of errors or other problems.
In my case, it's MFC project that has console window (AllocConsole). After commented out that one, started receiving almost all of packets sent from server
(I've read that standard file IO takes too much time to write and read to console window, so my guess is while it's busy to print texts, missing to recv packets)

Linux socket acting as Multicast Server and Client

I used two sockets in the same process, one acting as multicast server and the other acting as multicast client. I am trying to combine both server and client functionality in a single socket. Could not succeed till now. Did any one tried making a single socket as multicast server and client. Is it possible? If not any reference that states the limitation would be appreciated. Thank a lot.
It is possible, at least on windows. Try following example(without error checking):
SOCKET the_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("192.168.1.117");
service.sin_port = htons(56565);
int ret = bind(the_socket, (SOCKADDR *) &service, sizeof (service));
unsigned int grpaddr = inet_addr("224.4.4.44");
unsigned int iaddr = inet_addr("192.168.1.117");
//You should have the equivalent function in your existing client
//to join the multicast group
ret = join_source_group(the_socket, grpaddr, service.sin_addr.s_addr, iaddr);
std::string data = "AA";
sockaddr_in group;
group.sin_family = AF_INET;
group.sin_addr.s_addr = inet_addr("224.4.4.44");
group.sin_port = htons(56565);
ret = sendto(the_socket, data.c_str(), data.length(), 0, (const sockaddr*)&group, sizeof(group));
char recvdata[3] = {0};
ret = recv(the_socket, recvdata, 3, 0);
leave_source_group(the_socket, grpaddr, service.sin_addr.s_addr, iaddr);
"Server" and "Client" are not terms which really fit very comfortably into non connection-based sockets. Particularly with multicasting.
So a "Multicast receiver" and "Multicast sender" might be more appropriate.
You can of course, use the same socket to do both at once. It is not mandatory to join a group to send messages to it, but if you do, you may receive your own messages (in some OSs this is optional, for instancce using Linux's setsockopt IP_MULTICAST_LOOP).
"Server" and "client" are really concepts which work at a higher layer than multicasting. I hope that you can explain what you are trying to do better, maybe in another question.
Whether a multicast sender or receiver is the client or server, depends entirely on the application.

WSARecvFrom on unconnected UDP socket does not return

I am writing a small program that tests an UDP network service. The implementation of the service is allowed to create a new socket for the session and respond to the client from there, at which point the client is then required to talk to this address (similar to TFTP).
Minimal client sans error checking looks like this:
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in destaddr = { ... };
MSGBUF msg[] = { ... };
DWORD sent;
WSASendTo(fd, msg, sizeof msg / sizeof *msg, &sent, 0, (sockaddr *)sa, sizeof sa, 0, 0);
char buffer[4096];
MSGBUF rcvmsg = { sizeof buffer, buffer };
DWORD received;
sockaddr_storage sa;
socklen_t sa_len = sizeof sa;
DWORD flags = 0;
WSARecvFrom(fd, &rcvmsg, 1, &received, &flags, (sockaddr *)&sa, &sa_len, 0, 0);
The client works fine if the server responds from the same address and port that the initial message was sent to, however replies from another port are silently discarded and the client hangs in WSARecvFrom.
Explicitly binding the socket to { AF_INET, INADDR_ANY, 0 } to force assignment of a local port, or invoking listen(fd, 5); makes no difference, as expected.
Is there anything in WSASendTo that implicitly connects an UDP socket, and if so, what should I do to avoid this?
UDP doesn't have connections. Datagrams are sent to and from ports; it's one-way communication.
It sounds to me like your server is letting itself be assigned a temporary port (i.e. passing 0 as the port in sockaddr_in), instead of using a specific port. This won't work.
Since UDP has no concept of a connection, each time you send data, it could be sent from a different port; the first send doesn't reserve the port that it was given, it just sends a datagram from it and then lets it go.
Your server should be binding itself to a specific port.
Meh, it was a firewall issue. Adding the application to the list of programs allowed to receive incoming traffic fixed the issue.

How to get your own (local) IP-Address from an udp-socket (C/C++)

You have multiple network adapters.
Bind a UDP socket to an local port, without specifying an address.
Receive packets on one of the adapters.
How do you get the local ip address of the adapter which received the packet?
The question is, "What is the ip address from the receiver adapter?" not the address from the sender which we get in the
receive_from( ..., &senderAddr, ... );
call.
You could enumerate all the network adapters, get their IP addresses and compare the part covered by the subnet mask with the sender's address.
Like:
IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
foreach( adapter in EnumAllNetworkAdapters() )
{
adapterSubnet = adapter.subnetmask & adapter.ipaddress;
senderSubnet = adapter.subnetmask & senderAddr;
if( adapterSubnet == senderSubnet )
{
return adapter.ipaddress;
}
}
}
The solution provided by timbo assumes that the address ranges are unique and not overlapping. While this is usually the case, it isn't a generic solution.
There is an excellent implementation of a function that does exactly what you're after provided in the Steven's book "Unix network programming" (section 20.2)
This is a function based on recvmsg(), rather than recvfrom(). If your socket has the IP_RECVIF option enabled then recvmsg() will return the index of the interface on which the packet was received. This can then be used to look up the destination address.
The source code is available here. The function in question is 'recvfrom_flags()'
G'day,
I assume that you've done your bind using INADDR_ANY to specify the address.
If this is the case, then the semantics of INADDR_ANY is such that a UDP socket is created on the port specified on all of your interfaces. The socket is going to get all packets sent to all interfaces on the port specified.
When sending using this socket, the lowest numbered interface is used. The outgoing sender's address field is set to the IP address of that first outgoing interface used.
First outgoing interface is defined as the sequence when you do an ifconfig -a. It will probably be eth0.
HTH.
cheers,
Rob
Unfortunately the sendto and recvfrom API calls are fundamentally broken when used with sockets bound to "Any IP" because they have no field for local IP information.
So what can you do about it?
You can guess (for example based on the routing table).
You can get a list of local addresses and bind a seperate socket to each local address.
You can use newer APIs that support this information. There are two parts to this, firstly you have to use the relavent socket option (ip_recvif for IPv4, ipv6_recvif for IPv6) to tell the stack you want this information. Then you have to use a different function (recvmsg on linux and several other unix-like systems, WSArecvmsg on windows) to receive the packet.
None of these options are great. Guessing will obviously produce wrong answers soemtimes. Binding seperate sockets increases the complexity of your software and causes problems if the list of local addresses changes will your program is running. The newer APIs are the correct techical soloution but may reduce portability (in particular it looks like WSArecvmsg is not available on windows XP) and may require modifications to the socket wrapper library you are using.
Edit looks like I was wrong, it seems the MS documentation is misleading and that WSArecvmsg is available on windows XP. See https://stackoverflow.com/a/37334943/5083516
In Linux environment, you can use recvmsg to get local ip address.
//create socket and bind to local address:INADDR_ANY:
int s = socket(PF_INET,SOCK_DGRAM,0);
bind(s,(struct sockaddr *)&myAddr,sizeof(myAddr)) ;
// set option
int onFlag=1;
int ret = setsockopt(s,IPPROTO_IP,IP_PKTINFO,&onFlag,sizeof(onFlag));
// prepare buffers
// receive data buffer
char dataBuf[1024] ;
struct iovec iov = {
.iov_base=dataBuf,
.iov_len=sizeof(dataBuf)
} ;
// control buffer
char cBuf[1024] ;
// message
struct msghdr msg = {
.msg_name=NULL, // to receive peer addr with struct sockaddr_in
.msg_namelen=0, // sizeof(struct sockaddr_in)
.msg_iov=&iov,
.msg_iovlen=1,
.msg_control=cBuf,
.msg_controllen=sizeof(cBuf)
} ;
while(1) {
// reset buffers
msg.msg_iov[0].iov_base = dataBuf ;
msg.msg_iov[0].iov_len = sizeof(dataBuf) ;
msg.msg_control = cBuf ;
msg.msg_controllen = sizeof(cBuf) ;
// receive
recvmsg(s,&msg,0);
for( struct cmsghdr* pcmsg=CMSG_FIRSTHDR(&msg);
pcmsg!=NULL; pcmsg=CMSG_NXTHDR(&msg,pcmsg) ) {
if(pcmsg->cmsg_level==IPPROTO_IP && pcmsg->cmsg_type==IP_PKTINFO) {
struct in_pktinfo * pktinfo=(struct in_pktinfo *)CMSG_DATA(pcmsg);
printf("ifindex=%d ip=%s\n", pktinfo->ipi_ifindex, inet_ntoa(pktinfo->ipi_addr)) ;
}
}
}
The following does not work in asymmetric routing environment.
you can first set SO_REUSEADDR to true
BOOL bOptVal = 1;
setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));
after receive_from( ..., &remoteAddr, ... ); create another socket, and connect back to remoteAddr. Then call getsockname can get the ip address.
SOCKET skNew = socket( )
// Same local address and port as that of your first socket
// INADDR_ANY
bind(skNew, , )
// set SO_REUSEADDR to true again
setsockopt(skNew, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));
// connect back
connect(skNew, remoteAddr)
// get local address of the socket
getsocketname(skNew, )
ssize_t
recvfrom(int socket, void *restrict buffer, size_t length, int flags,
struct sockaddr *restrict address, socklen_t *restrict address_len);
ssize_t
recvmsg(int socket, struct msghdr *message, int flags);
[..]
If address is not a null pointer and the socket is not connection-oriented, the
source address of the message is filled in.
Actual code:
int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);
fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));
hope this helps.
Try this:
gethostbyname("localhost");