I have to write a C app that will act as both UDP sender & receiver. The program should first detect the broadcast address, then send the message JOIN (name is read), 1 time/minute, then some messages. I understood the part with broadcasting packages. I can’t figure out how to turn my sender into a receiver also.
My code so far:
—old code —
Edit:
I now know that I have solve this without select() or poll(), but with fork()..Something like creating 2 separate processes that will deal with writing to the socket and, respectively, reading data from the socket. I manage to send data. I have some troubles reading data from the socket. I receive smth like the last characters of the string I send. Also, I have an error “:Invalid argument”..I’ve tried debugging but I don’t really know where this error comes from. It appears just after I do the fork(). I have to be able to ignore mssages that I send somehow, but testing this locally it’s confusing (I have to receive messages from others and also, know the IP address that sent me the message). Now, if I start the app twice, In one app I don’t receive messages from the other, just the last characters of what the app sends.
Here’s my code:
#includes..
#define MAXLEN 100
#define MAXNAME 20
#define PORT 8888
#define MAX 20
int sockfd;
struct sockaddr_in socket_in; //connector's info
char name[MAXNAME];
int numbytes;
char senders[MAX];
void sendJoin (int signal) {
if ((numbytes=sendto(sockfd, name, strlen(name), 0,
(struct sockaddr *)&socket_in, sizeof(socket_in))) == -1) {
perror("sendto");
exit(1);
}
printf("Sent %s to %s (%d bytes)\n", name,inet_ntoa(socket_in.sin_addr),numbytes);
alarm(5);
}
void leaveGroup(int signal) {
/* Broadcast LEAVE and end execution */
char msg[10];
strcpy(msg, "LEAVE\0");
if ((numbytes=sendto(sockfd, msg, strlen(msg), 0,
(struct sockaddr *)&socket_in, sizeof(socket_in))) == -1) {
perror("sendLEAVE");
exit(1);
}
printf("\nSent LEAVE message to %s (%d bytes)\n", inet_ntoa(socket_in.sin_addr),numbytes);
scanf("%s", msg);
close(sockfd);
exit(1);
}
void read_socks() {
char message[MAXLEN];
int size = sizeof(socket_in);
numbytes = recvfrom (sockfd, &message, 1, 0,
(struct sockaddr*) &socket_in, &size);
if(strcmp(message, name)!=0) {
printf("Received: %s\n", message);
}
}
int main(void){
struct hostent *hst;
int broadcast = 1;
if ((hst=gethostbyname("192.168.240.255")) == NULL) { // get the host info
perror("gethostbyname");
exit(1);
}
if((sockfd=socket(AF_INET, SOCK_DGRAM, 0))==-1) {
perror("socket");
exit(1);
}
if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))==-1){
perror("setsockopt(SO_BROADCAST)");
exit(1);
}
memset(&socket_in, 0, sizeof(socket_in));
socket_in.sin_family = AF_INET;
socket_in.sin_port = htons(PORT);
socket_in.sin_addr = *((struct in_addr *)hst->h_addr);
signal(SIGINT, leaveGroup);
signal(SIGALRM, sendJoin);
printf("NAME: ");
fgets(name,MAXNAME,stdin);
strcpy(name,strcat("JOIN: ", name));
name[strlen(name)-1]='\0';
if ((numbytes=sendto(sockfd, name, strlen(name), 0,
(struct sockaddr *)&socket_in, sizeof(socket_in))) == -1) {
perror("sendto");
exit(1);
}
printf("Sent %s to %s (%d bytes)\n", name,inet_ntoa(socket_in.sin_addr),numbytes);
alarm(5);
while (1)
{
int pid = fork();
if(pid==0){ //Child process reads from the socket
memset(&socket_in, 0, sizeof(socket_in));
socket_in.sin_family = AF_INET;
socket_in.sin_port = htons(PORT);
socket_in.sin_addr = *((struct in_addr *)hst->h_addr);
sockfd = bind(sockfd, (struct sockaddr*) &socket_in, sizeof(socket_in));
if(sockfd==-1) perror("Bind error. Port is already in use!");
read_socks();
exit(0);
}
else if(pid > 0){ //Parent process sends data through the socket
memset(&socket_in, 0, sizeof(socket_in));
socket_in.sin_family = AF_INET;
socket_in.sin_port = htons(PORT);
socket_in.sin_addr = *((struct in_addr *)hst->h_addr);
if ((numbytes=sendto(sockfd, name, strlen(name), 0,
(struct sockaddr *)&socket_in, sizeof(socket_in))) == -1) {
perror("sendto");
exit(1);
}
}
sleep(4);
}
close(sockfd);
return 0;
}
I would greatly appreaciate some suggestions.
What you need to do is suspend your program until one of two things happen:
The solution on unix, if you want to suspend until one of multiple things happen, is the function
select().Another possible solution in your case is to use non-blocking I/O and check for new packages once in a while — but that’s basically active polling and active polling is bad, bad, bad.
The problem you face with your code is that your program is blocked in
recvfrom()until it gets a message. (Note: I’m not totally sure whether your SIGALRM method should indeed work during blocked I/O wait but I assume the signal gets queued until your reading operation is finished (data received))