UDP отправитель и получатель одновременно C - PullRequest
1 голос
/ 31 марта 2011

Мне нужно написать приложение на C, которое будет одновременно и отправителем, и получателем UDP. Программа должна сначала определить широковещательный адрес, затем отправить сообщение JOIN (имя читается), 1 раз в минуту, затем некоторые сообщения. Я понял часть с вещательными пакетами. Я не могу понять, как превратить моего отправителя в получателя. Мой код до сих пор: --- старый код ---

Edit: Теперь я знаю, что мне нужно решить эту проблему без select () или poll (), но с помощью fork (). Что-то вроде создания 2 отдельных процессов, которые будут заниматься записью в сокет и, соответственно, чтением данных из сокета. Мне удается отправить данные. У меня проблемы с чтением данных из сокета. Я получаю что-то вроде последних символов строки, которую я отправляю. Кроме того, у меня есть ошибка ": Неверный аргумент" .. Я пытался отладить, но я действительно не знаю, откуда эта ошибка. Появляется сразу после того, как я делаю fork (). Я должен иметь возможность игнорировать сообщения, которые я каким-либо образом отправляю, но это локальное тестирование сбивает с толку (я должен получать сообщения от других, а также знать IP-адрес, который отправил мне сообщение). Теперь, если я запускаю приложение дважды, в одном приложении я не получаю сообщения от другого, только последние символы того, что приложение отправляет. Вот мой код:

#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;
}

Я бы очень оценил некоторые предложения.

Ответы [ 3 ]

1 голос
/ 31 марта 2011

Вам нужен цикл выбора или опроса. Есть много вопросов StackOverflow, касающихся этих вопросов.

Я также рекомендую установить неблокируемые сокеты, чтобы избежать некоторых редких проблем.

Или, если вы пишете в Windows, вы можете настроить свою сеть на использование событий.

0 голосов
/ 31 марта 2011

select(2), poll(2) и родственники позволяют вам ждать событий в файловых дескрипторах и тайм-аутах.

При использовании одного из этих системных вызовов нет необходимости использовать alarm(). alarm() иногда используется для запуска вызова read(2) или write(2) с таймаутом (хотя я предпочитаю использовать select() даже в этом случае, это означает, что мне все равно, кто еще может использовать SIGALARM) .

И poll(), и select() возвращают 0 по истечении времени ожидания. Некоторые реализации select() обновляют параметр timeout оставшимся временем, другие - нет. В любом случае, поскольку select() или poll() могли проснуться раньше для обработки какого-либо другого события (или потому, что они были прерваны сигналом), правильная вещь, которую нужно сделать, это проверить, действительно ли истекло время ожидания, или пересчитать время ожидания, что-то, что традиционно делалось с использованием time(2) или gettimeofday(2), но что в наше время лучше делать с использованием clock_gettime(2) с часами CLOCK_MONOTONIC, которое не зависит от настроек часов (date(1), високосных секунд, дневного света) экономя время, ...).

0 голосов
/ 31 марта 2011

Что вам нужно сделать, это приостановить вашу программу, пока не произойдет одно из двух:

  • кто-то отправит вам UDP-сообщение
  • минута истечет (вам нужно отправить свойшироковещательное сообщение)

Решение Unix, если вы хотите приостановить работу до тех пор, пока не произойдет одно из множества событий, это функция select().

Другое возможное решение в вашем случае заключается виспользуйте неблокирующий ввод-вывод и время от времени проверяйте наличие новых пакетов - но это в основном активный опрос, а активный опрос - это плохо, плохо, плохо.

Проблема, с которой вы сталкиваетесь в своем коде, заключается в том, что ваша программазаблокирован в recvfrom(), пока не получит сообщение. (Примечание: я не совсем уверен, должен ли ваш метод SIGALRM действительно работать во время ожидания заблокированного ввода-вывода, но я предполагаю, что сигнал ставится в очередь до завершения операции чтения (данные получены))

...