How to check whether a client has joined a multicast group? - c

End goal:
For the parent process of the server to know what clients join or leave the multicast group. So far I've only tried to check on clients joining, for I assume checking on those leaving is a similar matter.
My method(s) so far:
Checking for changes in the socket of the multicast group through select().
After joining, the client executes a sendto() directed to the (parent process of) the server program. The select() in the server is meant to recognize any changes, but apparently doesn't, and thus retval != 0 is never true.
Results so far
I Have tried with many different IP addresses and constants such as INADDR_ANY, but I've only managed as far as sending a message back to the client through the multicast, and this one interpret it as if the server-program had sent it. The most common result is the server-program not receiving any message at all.
Here is my client code:
/* Receiver/client multicast Datagram*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_MSG 100
struct sockaddr_in localSock, servSock;
struct ip_mreq group;
int sd, n;
int datalen, mcastport;
char msg[MAX_MSG];
int main(int argc, char *argv[])
{
if(argc!=3) {
printf("usage : %s <address> <port>\n",argv[0]);
exit(0);
}
mcastport = atoi(argv[2]);
/* Create a datagram socket on which to receive. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening datagram socket....OK.\n");
/* Enable SO_REUSEADDR to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEADDR error");
close(sd);
exit(1);
}
else
printf("Setting SO_REUSEADDR...OK.\n");
}
/* Bind to the proper port number with the IP address */
/* specified as INADDR_ANY. */
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(mcastport);
localSock.sin_addr.s_addr = INADDR_ANY;
if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
{
perror("Binding datagram socket error");
close(sd);
exit(1);
}
else
printf("Binding datagram socket...OK.\n");
printf("Enter the group's name you want to join:\n");
scanf("%s", msg);
/* Join the multicast group 226.1.1.1 on the local IP address */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
group.imr_multiaddr.s_addr = inet_addr(argv[1]);
group.imr_interface.s_addr = inet_addr("127.0.0.1");
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
close(sd);
exit(1);
}
else
printf("Adding multicast group...OK.\n");
/* Initialize the group sockaddr structure with a */
/* group address of 226.1.1.1 and port given by user. */
memset((char *) &servSock, 0, sizeof(servSock));
servSock.sin_family = AF_INET;
servSock.sin_addr.s_addr = inet_addr(argv[1]);
servSock.sin_port = htons(mcastport);
if(sendto(sd, "", 1, 0, (struct sockaddr*) &servSock, sizeof(servSock)) < 0)
{perror("Sending datagram message error");}
else
printf("Sending datagram message...OK\n");
/* Read from the socket. */
if((n=read(sd, msg, MAX_MSG)) < 0)
{
perror("Reading datagram message error");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message...OK.\n");
printf("The message from multicast server is: \"%s\"\n", msg);
}
msg[n] = '\0';
return 0;
}
This is my server program code:
/* Send Multicast Datagram code*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h> /* for strncpy, memset */
#define MAX_MSG 100
struct in_addr localInterface;
struct sockaddr_in groupSock, cliAddr;
int sd, mcastport, maxJoin, maxJoined = 0, pipefd[2], cliLen, cpid;
char msg[MAX_MSG], groupName[MAX_MSG];
int main (int argc, char *argv[ ])
{
/* check command line args */
if(argc < 2) {
printf("usage : %s <port> \n", argv[0]);
exit(1);
}
mcastport = atoi(argv[1]);
/* Create a datagram socket on which to send. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK\n");
/* Initialize the group sockaddr structure with a */
/* group address of 225.1.1.1 and port given by user. */
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
groupSock.sin_port = htons(mcastport);
printf("Create a group: ");
scanf("%s", groupName);
printf("Maximum number of clients that can join the group? ");
scanf("%d", &maxJoin);
/* Disable loopback so you do not receive your own datagrams.
{
char loopch = 0;
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
{
perror("Setting IP_MULTICAST_LOOP error");
close(sd);
exit(1);
}
else
printf("Disabling the loopback...OK.\n");
}
*/
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
printf("Setting the local interface...");
localInterface.s_addr = inet_addr("127.0.0.1");
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
{
perror("error");
exit(1);
}
else
printf("OK\n");
if((cpid = fork()) == 0) //child process --sends messages
{
/* Send a message to the multicast group specified by the*/
/* groupSock sockaddr structure. */
printf("Enter a message to send: \n");
scanf("%s", msg);
if(sendto(sd, msg, strlen(msg)+1, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0)
{perror("Sending datagram message error");}
else
printf("Sending datagram message...OK\n");
/* Try the re-read from the socket if the loopback is not disable
if(read(sd, databuf, datalen) < 0)
{
perror("Reading datagram message error\n");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message from client...OK\n");
printf("The message is: %s\n", databuf);
}
*/
exit(EXIT_SUCCESS);
}
else //parent process --checks for JOINs and QUITs
{
fd_set rfds;
struct timeval tv;
int retval, status;
while (waitpid(cpid, &status, WNOHANG) != cpid)
{
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(sd, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(sd+1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval != 0)
{
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
cliLen = sizeof(cliAddr);
int n;
if((n = recvfrom(sd, msg, MAX_MSG, 0, (struct sockaddr *) &cliAddr,&cliLen)) == -1)
perror("Some bullshit happened");
msg[n] = '\0';
printf("Client IP:port is: %s:%d", inet_ntoa(cliAddr.sin_addr), (int) ntohs(cliAddr.sin_port));
} else
{printf("no data.\n");}
}
exit(0);
}
return 0;
}
Possible solutions I haven't tried:
Maybe I shouldn't use the multicast group to send information client->server, but rather another type of connection? I'm just guessing here. I know you guys don't like doing anyone's work.
I have been at this "simple" issue for several hours and tried reading everywhere I could, including this question which seems very similar, but I haven't managed to solve it in any way. I'm completely striking out here.

If the client sends to the multicast group and the server expects to read that, the server needs to join the multicast group as well.
It isn't a great solution, as all the other client members will also receive that multicast.
It would make more sense for the client to first receive a multicast from the server, and then to respond to the server's address, which is provided via the result arguments of recvfrom().

Two points that may help you:
There is no need to use multicasting if your communication is only local (you use 127.0.0.1 in your code)
Multicast groups are joined by network interfaces, not by processes. Once a network interface has joined a multicast interface, a second joining of the same network interface to the same multicast group will change nothing. Even when it's another process on the same host that requests the second
joining.

Related

Is it possible to launch multiple servers listening to a different IP socket address?

I'm trying to launch multiple servers, at once, in a c program. For the sake of simplicity let's say 5 servers.
If I understand well the sockets, each of them must be listening to a different IP socket address (different PORT, different IP interface address).
I thought to do that inside a loop, incrementing port number by i at each turn. Here's my current code just to launch one server. I know it's possible with bash by launching the same process in background, but in C I really don't know how to do that and if it's even possible
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
long PORT;
int main(int argc, char const *argv[])
{
/* 1. Open a socket
2. Bind to a address(and port).
3. Listen for incoming connections.
4. Accept connections
5. Read/Send
*/
int listenerSocket; /* socket for accepting connections */
int clientSocket; /* socket connected to client */
struct sockaddr_in server;
struct sockaddr_in client; /* client address information */
char buf[100]; /* buffer for sending & receiving data */
int errnum;
listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
if(listenerSocket == -1){
perror("erreur lors de la création du socket");
}
PORT = strtol(argv[1], NULL, 10);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port= htons(PORT);
if (bind(listenerSocket, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
puts("Server waiting for connection...");
while(1){
if (listen(listenerSocket, 5) < 0){
perror("listen failed");
exit(EXIT_FAILURE);
}
int c = sizeof(client);
if((clientSocket = accept(listenerSocket, (struct sockaddr*) &client, &c)) < 0){
puts("error accepting the request");
perror("Accept()");
}
puts("connection accepted");
while(1){
if( recv(clientSocket, buf, sizeof(buf), 0) < 0) {
errnum = errno;
perror("Recv()");
printf("val printed by errno: %d\n",errno);
}
printf("Message : %s\n", buf);
}
if (send(clientSocket, buf, sizeof(buf), 0) < 0)
{
perror("Send()");
exit(7);
}
close(clientSocket);
close(listenerSocket);
printf("Server ended successfully\n");
exit(0);
}

Multicasting in C macOS

Multicast between 2 applications on same host(macOS). The example is based on launching 2 applications Appli_A who join the same multicast group and are listening of an incoming datagram packet which is beeing send by the application Appli_B but there is only one of the 2 applications Appli_A receiving the packet and not both.
Since the implementation is platform dependent and I'm using macOS I had to change some commands that's why I use SO_REUSEPORT instead of SO_REUSEADDRES
But it still won't work. Here's a small example:
Appli_A
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct sockaddr_in localSock;
struct ip_mreq group;
int sd;
int datalen;
char databuf[1024];
void sigint_routine_handler(int param){
if(close(sd) == 0)
printf("hSocket closed\n");
else
printf("Error closing hSocket\n");
}
int main(int argc, char *argv[])
{
if(signal(SIGINT, sigint_routine_handler) == SIG_ERR)
printf("ERROR: Installing suspend handler SIGINT\n");
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
/* Enable SO_REUSEPORT to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, (char *)&reuse, sizeof(reuse)) < 0)
{
close(sd);
exit(1);
}
/* Since I'm using htonl(INADDR_ANY) I'm Binding the socket to all available interfaces */
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(54011);
localSock.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
{
close(sd);
exit(1);
}
/* Join the multicast group 235.73.158.23 */
group.imr_multiaddr.s_addr = inet_addr("235.73.158.23");
group.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
close(sd);
exit(1);
}
/* Read from the socket. */
datalen = sizeof(databuf);
if(read(sd, databuf, datalen) < 0)
{
perror("Reading datagram message error");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message...OK.\n");
printf("The message from multicast server is: \"%s\"\n", databuf);
}
if(close(sd) < 0)
printf("Error close socket descriptio\n");
return 0;
}
Appli_B
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct in_addr localInterface;
struct sockaddr_in groupSock;
int sd;
char databuf[50] = "Multicast test message$";
int datalen = sizeof(databuf);
int main(int argc, char *argv[])
{
/* Create a datagram socket on which to send. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK.\n");
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
/* README Normally here should I specifiy the Multicast-adress of the group for the outgoing
Datagrampacket, but it won't work if I specify this Multicast-adress "235.73.158.23"*/
groupSock.sin_addr.s_addr = htonl(INADDR_ANY);
groupSock.sin_port = htons(54011);
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface -> I have checked the interface, it is Multicast capable */
localInterface.s_addr = inet_addr("192.168.1.5");
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
{
perror("Setting local interface error");
exit(1);
}
else
printf("Setting the local interface...OK\n");
/* Send a message to the multicast group specified by the*/
/* groupSock sockaddr structure. */
int datalen = 50;
if(sendto(sd, databuf, datalen, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0){
perror("Sending datagram message error");
}
else
printf("Sending datagram message...OK\n");
return 0;
}
UPDATE
I have tried it on ubuntu linux 16.04, there everything works fine.
I changed SO_REUSEPORT to SO_REUSEADDR and the Multicast-address of the outgoing Datagrampacket of Appli_B to the Multicastgroup specified in Appli_A, if I do this on MACOS none of the 2 Appli_A applications receives the datagram packet, in case that the interface might not have a multicast address I followed this blog but it still won't work under MacOS.

Multicasting in C: Binary does not receive when using addrinfo

I have this funny little problem in two nearly identical programs. What I am trying to do is send some data on Multicast socket and receive it. For now, I am okay if the sender receives the message (I'll set the option to not receive later).
I have two implementation cases. In the first approach, I am using the traditional way of initializing a sockaddr structure and then binding to, and also joining a multicast group on the same socket. This, however, is IPv4/IPv6 dependent and to circumvent that, I tried to use addrinfo structures in the second variant of the program. Both programs are given below.
The problem is, the messages are being received in the first use case, where I am using the regular sockaddr while, there is no message being received/socket descriptor being set in the second case. Could somebody help me out and explain why is this happening?
Variant 1 (with sockaddr)
#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h> /* for nonblocking */
#include <netinet/tcp.h>
fd_set hm_tprt_conn_set;
main()
{
struct ip_mreq mreq;
struct sockaddr_in mc_addr;
int sock_fd ;
int val;
int reuse = 1;
struct sockaddr_in ip;
struct sockaddr_in src_addr;
int total_bytes_rcvd=0;
unsigned int length;
unsigned char buf[50];
int op_complete = 0;
int os_error;
struct timeval select_timeout;
fd_set read_set;
int32_t nready; //Number of ready descriptors
time_t time_val;
length = sizeof (src_addr);
sock_fd = socket(AF_INET, SOCK_DGRAM,0);
if(sock_fd == -1)
{
printf("\n Error Opening UDP MCAST socket");
perror("\n Cause is ");
exit(0);
}
printf("\n Setting the socket to non-blocking mode");
val = fcntl(sock_fd, F_GETFL , 0);
val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);
if (val == -1)
{
printf("\n Error while setting socket to non-blocking mode");
perror("Cause is ");
sock_fd = -1;
exit(0);
} //end if val == -1
if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
FD_SET(sock_fd, &hm_tprt_conn_set);
printf("\n Construct a mcast address structure");
/* construct a multicast address structure */
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
mc_addr.sin_port = htons(4936);
memset(&ip, 0, sizeof(ip));
ip.sin_family = AF_INET;
ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
ip.sin_port = htons(4936);
printf("\n Bind the multicast address structure and port to the recieving socket ");
if (bind( sock_fd, (struct sockaddr*) &mc_addr, sizeof(mc_addr)) == -1)
{
fprintf(stderr, "bind: %d\n", errno);
perror("\n Cause is ");
exit(0);
}
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("\n Cause is ");
exit(0);
}
printf("\nCreated Recv Socket: %d", sock_fd);
fflush(stdout);
memset(&src_addr, 0, sizeof(mc_addr));
while(1){
/* Send a multicast */
time_val = time(NULL);
snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
total_bytes_rcvd = sendto(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&ip,
length );
printf("\n%d bytes sent.", total_bytes_rcvd);
/* perform select */
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 5000000;
read_set = hm_tprt_conn_set;
nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
if(nready == 0)
{
/***************************************************************************/
/* No descriptors are ready */
/***************************************************************************/
continue;
}
else if(nready == -1)
{
perror("Error Occurred on select() call.");
continue;
}
if(FD_ISSET(sock_fd, &read_set))
{
printf("\n Recv the data");
total_bytes_rcvd = recvfrom(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&src_addr,
&length );
printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
printf("\n total byte recieved %d", total_bytes_rcvd);
/***************************************************************************/
/* If select returned 1, and it was a listen socket, it makes sense to poll*/
/* again by breaking out and use select again. */
/***************************************************************************/
if(--nready <=0)
{
printf("\nNo more incoming requests.");
continue;
}
}//end select on listenfd
}
}
Variant 2 (with addrinfo)
#include<stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h> /* for nonblocking */
#include <netinet/tcp.h>
#include <netdb.h> /* AI_PASSIVE and other Macros for getaddrinfo() */
fd_set hm_tprt_conn_set;
main()
{
struct addrinfo hints, *res, *ressave;
char target[128] = "127.0.0.1";
char service[128] = "4936";
struct ip_mreq mreq;
int sock_fd ;
int val;
int reuse = 1;
struct sockaddr_in ip;
struct sockaddr_in src_addr;
int total_bytes_rcvd=0;
unsigned int length;
unsigned char buf[50];
int op_complete = 0;
int os_error;
struct timeval select_timeout;
fd_set read_set;
int32_t nready; //Number of ready descriptors
time_t time_val;
length = sizeof (src_addr);
sock_fd = socket(AF_INET, SOCK_DGRAM,0);
if(sock_fd == -1)
{
printf("\n Error Opening UDP MCAST socket");
perror("\n Cause is ");
exit(0);
}
printf("\n Setting the socket to non-blocking mode");
val = fcntl(sock_fd, F_GETFL , 0);
val = fcntl(sock_fd, F_SETFL, val | O_NONBLOCK);
if (val == -1)
{
printf("\n Error while setting socket to non-blocking mode");
perror("Cause is ");
sock_fd = -1;
exit(0);
} //end if val == -1
if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
FD_SET(sock_fd, &hm_tprt_conn_set);
printf("\n Construct a mcast address structure");
/* construct a multicast address structure */
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
if((os_error = getaddrinfo(target, service, &hints, &res)) !=0)
{
printf("\n%s",gai_strerror(os_error));
exit(0);
}
ressave = res;
if(bind(sock_fd, res->ai_addr, res->ai_addrlen) != 0)
{
perror("Error binding to port");
close(sock_fd);
sock_fd = -1;
}
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.203");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) == -1)
{
fprintf(stderr, "setsockopt: %d\n", errno);
perror("Cause is ");
exit(0);
}
/* Set Destination address */
memset(&ip, 0, sizeof(ip));
ip.sin_family = AF_INET;
ip.sin_addr.s_addr = inet_addr("224.0.0.203")/*htonl(INADDR_ANY)*/;
ip.sin_port = htons(4936);
/* Set to zero address where addresses of sender will be received */
memset(&src_addr, 0, sizeof(src_addr));
while(1){
/* Send a multicast */
time_val = time(NULL);
snprintf(buf, sizeof(buf), "Hello: %s", ctime(&time_val));
total_bytes_rcvd = sendto(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&ip,
length );
printf("\n%d bytes sent.", total_bytes_rcvd);
/* perform select */
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 5000000;
read_set = hm_tprt_conn_set;
nready = select(sock_fd+1, &read_set, NULL, NULL, &select_timeout);
if(nready == 0)
{
/***************************************************************************/
/* No descriptors are ready */
/***************************************************************************/
continue;
}
else if(nready == -1)
{
perror("Error Occurred on select() call.");
continue;
}
if(FD_ISSET(sock_fd, &read_set))
{
printf("\n Recv the data");
total_bytes_rcvd = recvfrom(sock_fd,
buf,
sizeof(buf),
0,
(struct sockaddr *)&src_addr,
&length );
printf("%s: message = \" %s \"\n", inet_ntoa(src_addr.sin_addr), buf);
printf("\n total byte recieved %d", total_bytes_rcvd);
/***************************************************************************/
/* If select returned 1, and it was a listen socket, it makes sense to poll*/
/* again by breaking out and use select again. */
/***************************************************************************/
if(--nready <=0)
{
printf("\nNo more incoming requests.");
continue;
}
}//end select on listenfd
}
}
The difference is that in the first variant you're binding to INADDR_ANY, while in the second variant you're binding to 127.0.0.1. Failing to bind to INADDR_ANY means you won't receive any multicast data.
You can fix this with the following:
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
if((os_error = getaddrinfo(NULL, service, &hints, &res)) !=0)
{
printf("\n%s",gai_strerror(os_error));
exit(0);
}
From the man page for getaddrinfo regarding AI_PASSIVE:
If node is NULL, the network address in each socket structure is initialized according to the AI_PASSIVE flag, which is set in
hints.ai_flags. The network address in each socket structure will be
left unspecified if AI_PASSIVE flag is set. This is used by server
applications, which intend to accept client connections on any network
address. The network address will be set to the loopback interface
address if the AI_PASSIVE flag is not set. This is used by client
applications, which intend to connect to a server running on the same
network host.
While in this case you are sending to the same host, multicast data does not go out on the localhost interface by default. You would need to call setsockopt with the IP_MULTICAST_IF option to set the outgoing multicast interface.
With this change, I was able to send and receive with the second variant.
Before you can bind() you need to have a working socket. You will need to cycle through all the results. Here's what's missing on your code.
ressave = res;
sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
while(ressave != NULL && (sock < 0 || connect(sock, ressave->ai_addr, ressave->ai_addrlen) < 0)) {
close(sock);
if((ressave = ressave->ai_next) != NULL)
sock = socket(ressave->ai_family, ressave->ai_socktype, ressave->ai_protocol);
}
At this point you have either found a working socket sock or not. When ressave is not NULL then the value of socket sock is valid.

Linux TCP server, sending RAW data to few clients every X seconds

I'm writing simple TCP server and I found some issue. Maybe you can help me a bit.
So, I wrote an echo server first (to test connection with computer client). It's working okay, but now I need to change it a bit. Server should sent char[100] to client when it connects and sent same char[] to every client every X seconds/minutes.
I've trying many changes, but application only crashes. Commented some of my "mistakes" in this code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
/* BufferLength is 100 bytes */
#define BufferLength 100
/* Server port */
#define SERVPORT 6000
int main(){
/* Variable and structure definitions. */
int sd, wyslij, sd2, rc, length = sizeof(int);
int totalcnt = 0, on = 1;
char temp;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
struct sockaddr_in their_addr;
fd_set read_fd;
struct timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;
char datadata[100] = "This is a test string from server lol!!! ";
/* Get a socket descriptor */
if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("Server-socket() error");
/* exit */
exit (-1);
}else
printf("Server-socket() is OK\n");
/* Allow socket descriptor to be reusable */
if((rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0){
perror("Server-setsockopt() error");
close(sd);
exit (-1);
}else
printf("Server-setsockopt() is OK\n");
/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("Using %s, listening at %d\n", inet_ntoa(serveraddr.sin_addr), SERVPORT);
if((rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0){
perror("Server-bind() error");
/* Close the socket descriptor */
close(sd);
/* and just exit */
exit(-1);
}else
printf("Server-bind() is OK\n");
/* queue up to 10 clients */
if((rc = listen(sd, 10)) < 0){
perror("Server-listen() error");
close(sd);
exit (-1);
}else
printf("Server-Ready for client connection...\n");
/* accept() the incoming connection request. */
int sin_size = sizeof(struct sockaddr_in);
if((sd2 = accept(sd, (struct sockaddr *)&their_addr, &sin_size)) < 0){
perror("Server-accept() error");
close(sd);
exit (-1);
}else
printf("Server-accept() is OK\n");
/*client IP*/
printf("Server-new socket, sd2 is OK...\n");
printf("Got connection from the client: %s\n", inet_ntoa(their_addr.sin_addr));
/* Wait for up to 15 seconds on */
/* select() for data to be read. */
FD_ZERO(&read_fd);
FD_SET(sd2, &read_fd);
rc = select(sd2+1, &read_fd, NULL, NULL, &timeout);
/* rc = write(sd2, datadata, sizeof(datadata)); */
if((rc == 1) && (FD_ISSET(sd2, &read_fd))){
/* rc = write(sd2, datadata, sizeof(datadata)); */
/* Read data from the client. */
totalcnt = 0;
while(totalcnt < BufferLength){
/* read() from client */
rc = read(sd2, &buffer[totalcnt], (BufferLength - totalcnt));
if(rc < 0){
perror("Server-read() error");
close(sd);
close(sd2);
exit (-1);
}else if (rc == 0){
printf("Client program has issued a close()\n");
close(sd);
close(sd2);
exit(-1);
}
else{
totalcnt += rc;
printf("Server-read() is OK\n");
}
}
}else if (rc < 0){
perror("Server-select() error");
close(sd);
close(sd2);
exit(-1);
}
/* rc == 0 */
else{
printf("Server-select() timed out.\n");
close(sd);
close(sd2);
exit(-1);
}
/* Shows the data */
printf("Received data from the client: %s\n", buffer);
/* write() some bytes of string, */
/* back to the client. */
printf("Server-Echoing back to client...\n");
rc = write(sd2, datadata, sizeof(datadata));
if(rc != totalcnt){
perror("Server-write() error");
/* Get the error number. */
rc = getsockopt(sd2, SOL_SOCKET, SO_ERROR, &temp, &length);
if(rc == 0){
/* Print out the asynchronously */
/* received error. */
errno = temp;
perror("SO_ERROR was: ");
}else
printf("Server-write() is OK\n");
close(sd);
close(sd2);
exit(-1);
}
/* Close the connection to the client and */
/* close the server listening socket. */
close(sd2);
close(sd);
exit(0);
return 0;
}
Thanks a lot buddies!
You may want to check out D.J. Bernstein's tcpserver (see http://cr.yp.to/ucspi-tcp/tcpserver.html). Basically, you can simply run your C program under tcpserver, and tcpserver will handle everything as far as setting up the sockets, listing for incoming connections on whatever port you are using, etc. When an incoming connection arrives on the port that you specify, tcpserver will spawn an instance of your program and pipe incoming info from the client to your program's STDIN, and pipe outgoing info from your program's STDOUT back to the client. This way, you can concentrate on your program's core logic (and simply read/write to stdout/stdin), and let tcpserver handle all of the heavy lifting as far as the sockets, etc.
Well, I ran your program against a simple TCP client code and did not see any crash. So, you probably should add gdb info to that. Also, in the program, I don't see where your programs wakes up periodically (you do have a comment) and sends data to the client. You should also consider adding the client fd to the list of read fd set and have one common select() call. If the select() returns a read-event on the listener, then that is a new connection and you should call accept. If the select() returns a read-event on a child fd, then you have some data to read adn you should call recv()/read().

Global IPv6 adrress is unable to bind on Solaris

I am trying to bring up a sample code for IPv6 server on Solaris. When i give link local address, it is working fine. But when i give global address, it is failing to bind. Please tell me, that can we use global IPv6 address on Solaris ?
This is my code ....
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* the port users will be connecting to */
#define MYPORT 9000
/* how many pending connections queue will hold */
#define BACKLOG 10
int main(int argc, char *argv[ ])
{
/* listen on sock_fd, new connection on new_fd */
int sockfd, new_fd;
/* my address information */struct sockaddr_in6 their_addr;
socklen_t sin_size;
//struct sigaction sa;
int yes = 1;
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
{
perror("Server-socket() error lol!");
return 0;//exit(1);
}
else
printf("Server-socket() sockfd is OK...\n");
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("Server-setsockopt() error lol!");
return 0;//exit(1);
}else
printf("Server-setsockopt is OK...\n");
/* host byte order */
my_addr.sin6_family = AF_INET6;
/* short, network byte order */
my_addr.sin6_port = htons(MYPORT);
/* automatically fill with my IP */
inet_pton(AF_INET6,"2345:1111:aaaa::500",&my_addr.sin6_addr);
//inet_pton(AF_INET6,"fe80::203:baff:fe50:cbe5",&my_addr.sin6_addr);
my_addr.sin6_scope_id=5;
/* zero the rest of the struct */
if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1)
perror("Server-bind() error");
return 0;//exit(1);
}
else
printf("Server-bind() is OK...\n");
if(listen(sockfd, BACKLOG) == -1)
{
perror("Server-listen() error");
return 0;//exit(1);
}
printf("Server-listen() is OK...Listening...\n");
sin_size = sizeof(struct sockaddr_in6);
if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
perror("Server-accept() error");
//continue;
}
else
printf("Server-accept() is OK...\n");
printf("Server-new socket, new_fd is OK...\n");
printf("Server: Got connection from \n");
/* this is the child process */
/* child doesn’t need the listener */
char buf[1024];
int numbytes=0;
if((numbytes = recv(new_fd, buf, 1024, 0)) == -1)
{
perror("recv()");
return 1;//exit(1);
}
else
printf("Client-The recv() is OK...\n");
buf[numbytes] = '\0';printf("Client-Received: %s", buf);
if(send(new_fd, "This is a test string from server!\n", 37, 0) == -1)
perror("Server-send() error lol!");
/* parent doesn’t need this*/
close(new_fd);
printf("Server-new socket, new_fd closed successfully...\n");
return 0;
}
Thank you ....
Is 2345:1111:aaaa::500 configured on any of your interfaces? This is required before you can bind to that address.
2345:1111:aaaa::500 is the netmask, the IP is fe80::203:baff:fe50:cbe5
I think you will need scope id (2) to bind() to work, since it´s a Link-Local IPv6 address.
Malleus
I am not sure about on the Solaris, but on Windows 7 it requires Administrator priviledges to bind a socket to a global or multicast address.
Can you run your test with elevated priviledges?
-Jesse

Resources