开发者

UDP sender & receiver at the same time C

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(&soc开发者_如何学运维ket_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.


You need a select or poll loop. There are plenty of StackOverflow questions relating to these.

I also recommend setting your sockets to non-blocking to avoid some rare problems.

Or if you are writing on Windows you can set your networking up to use events.


What you need to do is suspend your program until one of two things happen:

  • somebody sends you a UDP message
  • the minute is up (you need to send your broadcast message)

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))


select(2), poll(2) and relatives let you wait for events on file descriptors and timeouts.

When using one of these syscalls, there is no need to use alarm(). alarm() is sometimes used to launch a read(2) or write(2) call with a timeout (though I prefer to use select() even in this case, it means I don't care who else might be using SIGALARM).

Both poll() and select() return 0 when the timeout expires. Some select() implementations update the timeout parameter with the remaining time, others don't. In any case, since select() or poll() may have awakened earlier for processing some other event (or because they were interrupted by a signal), the correct thing to do is checking if the timeout has really expired / recalculating the timeout, something which has traditionally been done using time(2) or gettimeofday(2), but which in modern times should be better done using clock_gettime(2) with the CLOCK_MONOTONIC clock, which is immune to clock adjustments (date(1), leap seconds, daylight saving time, ...).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜