I'm trying to get my head around socket programming and have encountered some unexpected (for me) behaviour.
When I try to send data to "localhost" and set addrinfo.ai_family to AF_INET the message I send isn't coming through from my client process to my host process (recvfrom() doesn't return). If I set it to AF_INET6 all is fine. Same for AF_UNSPEC in which case it picks the IPv6 addrinfo (first in the list). Both host and client use the same ai_family of course.
I've also tried this with code copy pasted from beej's guide to network programming which had the same result. I'm using DGRAM sockets.
I tried connecting from a different pc I got the opposite results, IPv4 worked fine, IPv6 did not. I gather this may be due to me using a '6to4 gateway'. I really have no idea what this means.
The problem is related to my own machine as the code does work over IPv4 on another machine I tested it on.
I can't say if it's a sending or receiving problem.
What could prevent me from sending or receiving data to/from localhost using AF_INET sockets?
I'm on a windows7 64bit machine compiling with MingW.
If it makes any difference I'm running the same program for host and client processes with different arguments. I ran the release and debug programs together (so it's not the same program twice) but got the same results.
Thanks in advance and apologies if this is considered a stupid question.
code:
typedef struct addrinfo addrinfo_t;
typedef struct sockaddr_storage sockaddr_storage_t;
typedef struct sockaddr_in sockaddr_in_t;
typedef struct sockaddr_in6 sockaddr_in6_t;
void connect_to_server(const char* server_name, const char* message)
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
if ((status = getaddrinfo(server_name, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
int bytes_sent = sendto(s, message, strlen(message), 0, res->ai_addr, res->ai_addrlen);
close(s);
printf("Sent %i bytes to port %i\n", bytes_sent, ((sockaddr_in_t*)res->ai_addr)->sin_port);
freeaddrinfo(res);
}
void setup_server()
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(NULL, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
//Bind the socket to own address (mostly the port number contained in the address)
if (bind(s, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "failed to bind, errno: %u\n", errno);
exit(1);
}
freeaddrinfo(res);
const size_t read_buffer_size = 1024;
void* read_buffer = malloc(read_buffer_size);
sockaddr_storage_t peer_address;
int peer_address_length = sizeof(sockaddr_storage_t);
sockaddr_storage_t own_sock_addr;
int own_sock_addr_len = sizeof(sockaddr_storage_t);
getsockname(s, (struct sockaddr*)&own_sock_addr, &own_sock_addr_len);
printf("Listening on port %i\n", ((sockaddr_in_t*)&own_sock_addr)->sin_port);
int bytes_received = recvfrom(s,
read_buffer,
read_buffer_size-1,
0,
(struct sockaddr*)&peer_address,
&peer_address_length );
printf("Received %i byte message:\n%s\n", bytes_received, (char*)read_buffer);
}
AF_INET is for IPv4, and AF_INET6 is for IPv6. When sending an IPv4 datagram, the receiver must be receiving data on the destination IP/port using either an IPv4 socket or an IPv6 dual stack socket (an IPv6 socket that accepts both IPv4 and IPv6 traffic). When sending an IPv6 datagram, the receiver must be receiving data using an IPv6 socket. Otherwise, the datagram will be ignored, So it sounds like the one machine is using an IPv6 socket that ignores your IPv4 datagram, and the other machine is using an IPv4 socket that ignores your IPv6 datagram.
When you are calling getaddrinfo(), you are specifying AF_UNSPEC as the address family in both client and server. AF_UNSPEC tells getaddrinfo() that you want both IPv4 and IPv6 addresses, so it returns a linked list that potentially contains multiple entries for all of the available IPv4 and IPv6 addresses. On the server side, you are creating a single listening socket for only the first entry in the list, which may be IPv4 or IPv6. On the client side, you are creating a single sending socket for only the first entry in the list, which may be IPv4 or IPv6. So the actual address families used in both operations are going to be random and may mismatch each other at times.
On the server side, you need to either:
use AF_INET or AF_INET6 directly, instead of AF_UNSPEC, and then code the client accordingly to match.
loop through the entire addrinfo list creating a separate listening socket for every entry. That way, clients can send data to any IP/Port family the server is listening on.
use AF_INET6 only when creating the listening socket(s), but then enable dual stack functionality on them (Vista+ only) so they can receive both IPv4 and IPv6 datagrams. You will then have to pay attention to the address family reported by the sockaddr that recvfrom() returns in order to know whether any given datagram is using IPv4 or IPv6.
On the client side, you need to use AF_INET or AF_INET6 directly, instead of AF_UNSPEC, depending on what the server is actually listening on. It does not make sense to use AF_UNSPEC for a UDP client socket (it does make sense for a TCP client socket), unless the UDP protocol you are implementing replies to each datagram with an ack. Without that, the client has no way to know whether the server is accepting IPv4 or IPv6 datagrams (unless the user tells the app). With acks, the client could loop through the returned addrinfo list, sending a datagram to an entry in the list, wait a few seconds for an ack, and if not received then move on to the next entry in the list, repeating as needed until an ack actually arrives or the list is exhausted.
Related
I am currently learning how to use the windows raw sockets.
I created a raw arp reply frame(includes all headers (ethernet+arp headers)), and when I send it using sendto function,
It fails and return SOCKET_ERROR with error code 10047.
The parameters I used to create the socket are as follows:
socket s = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
and also I changed the socket options as follows:
int on=1;
setsockopt(s,IPPROTO_IP, 2,(char*)&on,sizeof(on));
(By the way, '2' is equal to IP_HDRINCL, for some reason, visual studio didn't recognize it..)
I try to send the packet as follows:
socketaddr sa = { 0 };
int SentBytesCount = sendto(s, (char*)&arp_raw_msg,sizeof(Arp_Frame),0,&sa,sizeof(sa));
Where Arp_Frame is a struct that includes ethernet header+arp header+18 Bytes for padding.
After this call I get that SentBytesCount is equal to SOCKET_ERROR(-1), and no packet is sent.
Thank you for your help!
Winsock error 10047 is WSAEAFNOSUPPORT:
Address family not supported by protocol family.
An address incompatible with the requested protocol was used. All sockets are created with an associated address family (that is, AF_INET for Internet Protocols) and a generic protocol type (that is, SOCK_STREAM). This error is returned if an incorrect protocol is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, for example, in sendto.
You created an AF_INET (IPv4) socket, but you are not passing sendto() a valid sockaddr_in containing an IPv4 address and port, hence the error. You are passing it an empty socketaddr (what is that?) instead.
Any sockaddr_... struct you use with a socket must match what the socket's address family expects, as set by the socket() call (in your case, AF_INET, which uses sockaddr_in addresses).
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("destination IP address");
sa.sin_port = htons(Destination port number);
int SentBytesCount = sendto(s, (char*)&arp_raw_msg, sizeof(Arp_Frame), 0, (struct sockaddr*)&sa, sizeof(sa));
As for IP_HDRINCL, it is defined in ws2tcpip.h.
My goal is making a TCP/IP connection in a sender <-> server -> receiver fashion.
I have a server, sender and receiver. The server initial part looks like:
int main(int argc, char *argv[]){
int welcomeSocket, senderSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
if (2 != argc) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
welcomeSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(atoi(argv[1]));
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//int len=20;
//char sbuffer[len];
//inet_ntop(AF_INET, &(serverAddr.sin_addr), sbuffer, len);
//printf("address:%s\n",sbuffer);
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Bind the address struct to the socket ----*/
bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
/*---- Listen on the socket, with 5 max connection requests queued ----*/
if(listen(welcomeSocket,5)==0)
printf("Listening\n");
else
printf("Error\n");
/*---- Accept call creates a new socket for the incoming connection ----*/
addr_size = sizeof serverStorage;
senderSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
//~~~~Some more code~~~
}
however this sets up the server ip address as 0.0.0.0. I have no clue whether this is correct or not, but all until now I had manually set up the ip address as 127.0.1.1 which is what I get when I type hostname -i in the terminal.
So, currently I am having my sender connect to the same IP address, but since I am working all files in the same computer I don't know if it will work across other computers in the network with this weird ip address 0.0.0.0. Can someone clarify (and maybe help fix) this small issue to me? I have tried reading other solutions that try to explain the difference between 0.0.0.0 and 127.0.0.1 but I couldn't find anything related to the performance on the connection/communication between server and sender in a TCP/IP connection.
And then the IPv4 address is listed as 129.15.78.12 in my system settings, but then again, not sure which one should be used for the server or receiver.
The difference is that by using INADDR_ANY you bind the service to all interfaces, as explained in
http://man7.org/linux/man-pages/man7/ip.7.html
and
understanding INADDR_ANY for socket programming - c
Other than that, if you keep this config any computer trying to reach your server will connect if it uses a valid external IP address, like the one you mention you see in the system settings. Hope this clarifies your question.
Both connect() and bind() system calls 'associate' the socket file descriptor to an address (typically an ip/port combination). Their prototypes are like:-
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
and
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
What is the exact difference between 2 calls? When should one use connect() and when bind()?
Specifically, in some sample server client codes, found that client is using connect() and server is using the bind() call. Reason was not fully clear to me.
To make understanding better , lets find out where exactly bind and connect comes into picture,
Further to positioning of two calls , as clarified by Sourav,
bind() associates the socket with its local address [that's why server side binds, so that clients can use that address to connect to server.]
connect() is used to connect to a remote [server] address, that's why is client side, connect [read as: connect to server] is used.
We cannot use them interchangeably (even when we have client/server on same machine) because of specific roles and corresponding implementation.
I will further recommend to correlate these calls TCP/IP handshake .
So, who will send SYN here, it will be connect(). While bind() is used for defining the communication end point.
The one liner : bind() to own address, connect() to remote address.
Quoting from the man page of bind()
bind() assigns the address specified by addr to the socket referred to by the file descriptor sockfd. addrlen specifies the size, in bytes, of the address structure pointed to by addr. Traditionally, this operation is called "assigning a name to a socket".
and, from the same for connect()
The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr.
To clarify,
bind() associates the socket with its local address [that's why
server side binds, so that clients can use that address to connect
to server.]
connect() is used to connect to a remote [server] address, that's
why is client side, connect [read as: connect to server] is used.
I think it would help your comprehension if you think of connect() and listen() as counterparts, rather than connect() and bind(). The reason for this is that you can call or omit bind() before either, although it's rarely a good idea to call it before connect(), or not to call it before listen().
If it helps to think in terms of servers and clients, it is listen() which is the hallmark of the former, and connect() the latter. bind() can be found - or not found - on either.
If we assume our server and client are on different machines, it becomes easier to understand the various functions.
bind() acts locally, which is to say it binds the end of the connection on the machine on which it is called, to the requested address and assigns the requested port to you. It does that irrespective of whether that machine will be a client or a server. connect() initiates a connection to a server, which is to say it connects to the requested address and port on the server, from a client. That server will almost certainly have called bind() prior to listen(), in order for you to be able to know on which address and port to connect to it with using connect().
If you don't call bind(), a port and address will be implicitly assigned and bound on the local machine for you when you call either connect() (client) or listen() (server). However, that's a side effect of both, not their purpose. A port assigned in this manner is ephemeral.
An important point here is that the client does not need to be bound, because clients connect to servers, and so the server will know the address and port of the client even though you are using an ephemeral port, rather than binding to something specific. On the other hand, although the server could call listen() without calling bind(), in that scenario they would need to discover their assigned ephemeral port, and communicate that to any client that it wants to connect to it.
I assume as you mention connect() you're interested in TCP, but this also carries over to UDP, where not calling bind() before the first sendto() (UDP is connection-less) also causes a port and address to be implicitly assigned and bound. One function you cannot call without binding is recvfrom(), which will return an error, because without an assigned port and bound address, there is nothing to receive from (or too much, depending on how you interpret the absence of a binding).
bind tells the running process to claim a port. i.e, it should bind itself to port 80 and listen for incomming requests. with bind, your process becomes a server. when you use connect, you tell your process to connect to a port that is ALREADY in use. your process becomes a client. the difference is important: bind wants a port that is not in use (so that it can claim it and become a server), and connect wants a port that is already in use (so it can connect to it and talk to the server)
From Wikipedia http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29
connect():
The connect() system call connects a socket, identified by its file descriptor, to a remote host specified by that host's address in the argument list.
Certain types of sockets are connectionless, most commonly user datagram protocol sockets. For these sockets, connect takes on a special meaning: the default target for sending and receiving data gets set to the given address, allowing the use of functions such as send() and recv() on connectionless sockets.
connect() returns an integer representing the error code: 0 represents success, while -1 represents an error.
bind():
bind() assigns a socket to an address. When a socket is created using socket(), it is only given a protocol family, but not assigned an address. This association with an address must be performed with the bind() system call before the socket can accept connections to other hosts. bind() takes three arguments:
sockfd, a descriptor representing the socket to perform the bind on.
my_addr, a pointer to a sockaddr structure representing the address to bind to.
addrlen, a socklen_t field specifying the size of the sockaddr structure.
Bind() returns 0 on success and -1 if an error occurs.
Examples:
1.)Using Connect
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(){
int clientSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
socklen_t addr_size;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
clientSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(7891);
/* Set the IP address to desired host to connect to */
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.17");
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Connect the socket to the server using the address struct ----*/
addr_size = sizeof serverAddr;
connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
/*---- Read the message from the server into the buffer ----*/
recv(clientSocket, buffer, 1024, 0);
/*---- Print the received message ----*/
printf("Data received: %s",buffer);
return 0;
}
2.)Bind Example:
int main()
{
struct sockaddr_in source, destination = {}; //two sockets declared as previously
int sock = 0;
int datalen = 0;
int pkt = 0;
uint8_t *send_buffer, *recv_buffer;
struct sockaddr_storage fromAddr; // same as the previous entity struct sockaddr_storage serverStorage;
unsigned int addrlen; //in the previous example socklen_t addr_size;
struct timeval tv;
tv.tv_sec = 3; /* 3 Seconds Time-out */
tv.tv_usec = 0;
/* creating the socket */
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
printf("Failed to create socket\n");
/*set the socket options*/
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
/*Inititalize source to zero*/
memset(&source, 0, sizeof(source)); //source is an instance of sockaddr_in. Initialization to zero
/*Inititalize destinaton to zero*/
memset(&destination, 0, sizeof(destination));
/*---- Configure settings of the source address struct, WHERE THE PACKET IS COMING FROM ----*/
/* Address family = Internet */
source.sin_family = AF_INET;
/* Set IP address to localhost */
source.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY = 0.0.0.0
/* Set port number, using htons function to use proper byte order */
source.sin_port = htons(7005);
/* Set all bits of the padding field to 0 */
memset(source.sin_zero, '\0', sizeof source.sin_zero); //optional
/*bind socket to the source WHERE THE PACKET IS COMING FROM*/
if (bind(sock, (struct sockaddr *) &source, sizeof(source)) < 0)
printf("Failed to bind socket");
/* setting the destination, i.e our OWN IP ADDRESS AND PORT */
destination.sin_family = AF_INET;
destination.sin_addr.s_addr = inet_addr("127.0.0.1");
destination.sin_port = htons(7005);
//Creating a Buffer;
send_buffer=(uint8_t *) malloc(350);
recv_buffer=(uint8_t *) malloc(250);
addrlen=sizeof(fromAddr);
memset((void *) recv_buffer, 0, 250);
memset((void *) send_buffer, 0, 350);
sendto(sock, send_buffer, 20, 0,(struct sockaddr *) &destination, sizeof(destination));
pkt=recvfrom(sock, recv_buffer, 98,0,(struct sockaddr *)&destination, &addrlen);
if(pkt > 0)
printf("%u bytes received\n", pkt);
}
I hope that clarifies the difference
Please note that the socket type that you declare will depend on what you require, this is extremely important
Too Long; Don't Read: The difference is whether the source (local) or the destination address/port is being set. In short, bind() set the source and connect() set the destination. Regardless of TCP or UDP.
bind()
bind() set the socket's local (source) address. This is the address where packets are received. Packets sent by the socket carry this as the source address, so the other host will know where to send back its packets.
If receive is not needed the socket source address is useless. Protocols like TCP require receiving enabled in order to send properly, as the destination host send back a confirmation when one or more packets have arrived (i.e. acknowledgement).
connect()
TCP has a "connected" state. connect() triggers the TCP code to try to establish a connection to the other side.
UDP has no "connected" state. connect() only set a default address to where packets are sent when no address is specified. When connect() is not used, sendto() or sendmsg() must be used containing the destination address.
When connect() or a send function is called, and no address is bound, Linux automatically bind the socket to a random port. For technical details, take a look at inet_autobind() in Linux kernel source code.
Side notes
listen() is TCP only.
In AF_INET family, the socket's source or destination address (struct sockaddr_in) is composed by an IP address (see IP header), and TCP or UDP port (see TCP and UDP header).
According to the connect(2) man pages
If the socket sockfd is of type SOCK_DGRAM then serv_addr is the address to which datagrams are sent by default, and the only address from which datagrams are received. If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection to the socket that is bound to the address specified by serv_addr.
I am trying to filter packets from two different multicast groups that are being broadcasted on the same port and I thought connect() would have done the job but I can't make it work. In facts when I add it to my program I don't receive any packet. More info in this thread.
This is how I set the connect parameters:
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(multicast_addr);
mc_addr.sin_port = htons(multicast_port);
printf("Connecting...\n");
if( connect(sd, (struct sockaddr*)&mc_addr, sizeof(mc_addr)) < 0 ) {
perror("connect");
return -1;
}
printf("Receiving...\n");
while( (len = recv(sd, msg_buf, sizeof(msg_buf), 0)) > 0 )
printf("Received %d bytes\n", len);
Your program (probably) has the following problems:
you should be using bind() instead of connect(), and
you're missing setsockopt(..., IP_ADD_MEMBERSHIP, ...).
Here's an example program that receives multicasts. It uses recvfrom(), not recv(), but it's the same except you also get the source address for each received packet.
To receive from multiple multicast groups, you have three options.
First option: Use a separate socket for each multicast group, and bind() each socket to a multicast address. This is the simplest option.
Second option: Use a separate socket for each multicast group, bind() each socket INADDR_ANY, and use a socket filter to filter out all but a single multicast group.
Because you've bound to INADDR_ANY, you may still get packets for other multicast groups. It is possible to filter them out using the kernel's socket filters:
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/filter.h>
/**
* Adds a Linux socket filter to a socket so that only IP
* packets with the given destination IP address will pass.
* dst_addr is in network byte order.
*/
int add_ip_dst_filter (int fd, uint32_t dst_addr)
{
uint16_t hi = ntohl(dst_addr) >> 16;
uint16_t lo = ntohl(dst_addr) & 0xFFFF;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 16), // A <- IP dst high
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, hi, 0, 3), // if A != hi, goto ignore
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 18), // A <- IP dst low
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lo, 0, 1), // if A != lo, goto ignore
BPF_STMT(BPF_RET + BPF_K, 65535), // accept
BPF_STMT(BPF_RET + BPF_K, 0) // ignore
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
}
Third option: use a single socket to receive multicasts for all multicast groups.
In that case, you should do an IP_ADD_MEMBERSHIP for each of the groups. This way you get all packets on a single socket.
However, you need extra code to determine which multicast group a received packet was addressed to. To do that, you have to:
receive packets with recvmsg() and read the IP_PKTINFO or equivalent ancillary data message. However, to make recvmsg() give you this message, you first have to
enable reception of IP_PKTINFO ancillary data messages with setsockopt().
The exact thing you need to do depends on IP protocol version and OS. Here's how I did it (IPv6 code not tested): enabling PKTINFO and reading the option.
Here's a simple program that receives multicasts, which demonstrates the first option (bind to multicast address).
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXBUFSIZE 65536
int main (int argc, char **argv)
{
if (argc != 4) {
printf("Usage: %s <group address> <port> <interface address>\n", argv[0]);
return 1;
}
int sock, status, socklen;
char buffer[MAXBUFSIZE+1];
struct sockaddr_in saddr;
struct ip_mreq imreq;
// set content of struct saddr and imreq to zero
memset(&saddr, 0, sizeof(struct sockaddr_in));
memset(&imreq, 0, sizeof(struct ip_mreq));
// open a UDP socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket failed!");
return 1;
}
// join group
imreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
imreq.imr_interface.s_addr = inet_addr(argv[3]);
status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&imreq, sizeof(struct ip_mreq));
saddr.sin_family = PF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (status < 0) {
perror("bind failed!");
return 1;
}
// receive packets from socket
while (1) {
socklen = sizeof(saddr);
status = recvfrom(sock, buffer, MAXBUFSIZE, 0, (struct sockaddr *)&saddr, &socklen);
if (status < 0) {
printf("recvfrom failed!\n");
return 1;
}
buffer[status] = '\0';
printf("Received: '%s'\n", buffer);
}
}
The first thing to note is that multicast packets are sent to a multicast address, not from a multicast address.
connect() will allow (or not) packets received from a nominated address.
To configure your socket to receive multicast packets you need to use one of two socket options:
IP_ADD_MEMBERSHIP, or
IP_ADD_SOURCE_MEMBERSHIP
The former allows you to specify a multicast address, the latter allows you to specify a multicast address and source address of the sender.
This can be done using something like the following:
struct ip_mreq groupJoinStruct;
unsigned long groupAddr = inet_addr("239.255.0.1");
groupJoinStruct.imr_multiaddr.s_addr = groupAddr;
groupJoinStruct.imr_interface.s_addr = INADDR_ANY; // or the address of a specific network interface
setsockopt( yourSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &groupJoinStruct );
(error handling omitted for brevity)
To stop receiving multicast packets for this group address, use the socket options:
IP_DROP_MEMBERSHIP, or
IP_DROP_SOURCE_MEMBERSHIP
Note that a socket can have multiple multicast memberships. But, as the multicast address is the destination address of the packet, you need to be able to grab the destination address of the packet to be able to distinguish between packets for different multicast addresses.
To grab the destination address of the packet you'll need to use recvmsg() instead of recv() or recvfrom(). The destination address is contained within the IPPROTO_IP message level, of type DSTADDR_SOCKOPT.
As #Ambroz Bizjak has stated, you'll need to set the IP_PKTINFO socket option to be able to read this information.
Other things to check are:
Is multicast supported in your kernel? Check for the existence of /proc/net/igmp to ensure it's been enabled.
Has multicast been enable on your network interface? Check for "MULTICAST" listed when you run ifconfig on your interface
Does your network interface support multicast? Historically not all have. If not you may be able to get around this by setting your interface to promiscuous mode. e.g. ifconfig eth0 promisc
This should work as long as all the SENDING sockets are bound to the multicast address in question with bind. The address you specify in connect is matched against the SOURCE address of received packets, so you want to ensure that all packets have the same (multicast) SOURCE AND DESTINATION.
bind(2) each socket to the address of the respective multicast group and port instead of INADDR_ANY. That would do the filtering for you.
I'm having trouble figuring this out - I'm working with sockets in C using this guide - http://binarii.com/files/papers/c_sockets.txt
I'm trying to automatically get my ip and port using:
server.sin_port = 0; /* bind() will choose a random port*/
server.sin_addr.s_addr = INADDR_ANY; /* puts server's IP automatically */
...
...
bind(int fd, struct sockaddr *my_addr,int addrlen); // Bind function
After a successful bind, how do I find out what IP and Port I'm actually assigned?
If it's a server socket, you should call listen() on your socket, and then getsockname() to find the port number on which it is listening:
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1)
perror("getsockname");
else
printf("port number %d\n", ntohs(sin.sin_port));
As for the IP address, if you use INADDR_ANY then the server socket can accept connections to any of the machine's IP addresses and the server socket itself does not have a specific IP address. For example if your machine has two IP addresses then you might get two incoming connections on this server socket, each with a different local IP address. You can use getsockname() on the socket for a specific connection (which you get from accept()) in order to find out which local IP address is being used on that connection.
The comment in your code is wrong. INADDR_ANY doesn't put server's IP automatically'. It essentially puts 0.0.0.0, for the reasons explained in mark4o's answer.