Реализация UDP-клиента в C - отправка проблемы с датаграммой - PullRequest
0 голосов
/ 06 ноября 2019

Мне нужно реализовать UDP-клиент с DGRM-сокетами, при этом изменяя только части между /***TO BE DONE START***/ и /***TO BE DONE END***/, для реализации ping.

Нам дали адрес сервера и порт для тестирования наших программ - и адрес, и порт были одинаковыми для реализации TCP (уже сделано и работает правильно), но мы могли проверить его на других портах, какЧто ж.

Вот как должен выглядеть пример вывода:

UDP Ping trying to connect to server webdev.dibris.unige.it (130.251.61.30) on TCP port 1491
… connected to Pong server: asking for 501 repetitions of 16 _bytes UDP messages
Request = UDP 16 501

… Pong server agreed to ping-pong using port 62831 :-)
… about to connect socket 3 to IP address 130.251.61.30, port 62831
… sending message 1
Round trip time was 38.232 milliseconds in repetition 1
… sending message 2 

и т. Д. До повторения 501.

Однако вот как выглядит мой вывод (яукажите в комментариях недостающие части):

UDP Ping trying to connect to server webdev.dibris.unige.it (130.251.61.30) on TCP port 1491
… connected to Pong server: asking for 601 repetitions of 16 _bytes UDP messages
… Pong server agreed to ping-pong using port 62831 :-)
… sending message 1

и затем он останавливается, не давая никаких предупреждений или ошибок, и при этом не хватает "Request = UDP 16 501", "… about to connect socket 3 to IP address 130.251.61.30, port 62831" и, самое главное, каждого повторения.

Мои лучшиепредположение, что в отправляющей части моей реализации есть ошибка;однако я не смог его идентифицировать.

Вот полный код:

/*
#include "pingpong.h"

/*
* This function sends and wait for a reply on a socket.
* char message[]: message to send
* int messagesize: message length
*/

double do_ping(size_t msg_size, int msg_no, char message[msg_size], int ping_socket, double timeout)
{
   int lost_count = 0;
   char answer_buffer[msg_size];
   ssize_t recv_bytes, sent_bytes;
   struct timespec send_time, recv_time;
   double roundtrip_time_ms;
   int re_try = 0;

   /*** write msg_no at the beginning of the message buffer ***/
/*** TO BE DONE START ***/
   sprintf(message, "%d\n", msg_no);
/*** TO BE DONE END ***/

   do {
       debug(" ... sending message %d\n", msg_no);
   /*** Store the current time in send_time ***/
/*** TO BE DONE START ***/
           if(clock_gettime(CLOCK_REALTIME, &send_time)!=0) fail_errno("clock_gettime");
/*** TO BE DONE END ***/

   /*** Send the message through the socket ***/
/*** TO BE DONE START ***/
       sent_bytes = send(ping_socket, message, msg_size, 0);
       if(sent_bytes!=msg_size) fail_errno("send");
/*** TO BE DONE END ***/

   /*** Receive answer through the socket (non blocking mode) ***/
/*** TO BE DONE START ***/
       for (int offset = 0; (offset + (recv_bytes = recv(ping_socket, answer_buffer + offset, sent_bytes - offset, MSG_WAITALL))) < msg_size; offset += recv_bytes) {
           debug(" ... received %zd bytes back\n", recv_bytes);
           if (recv_bytes < 0)
               fail_errno("Error receiving data");
       }
/*** TO BE DONE END ***/

   /*** Store the current time in recv_time ***/
/*** TO BE DONE START ***/
           if(clock_gettime(CLOCK_REALTIME, &send_time)!=0) fail_errno("clock_gettime");
/*** TO BE DONE END ***/

       roundtrip_time_ms = timespec_delta2milliseconds(&recv_time, &send_time);

       for (;
            recv_bytes < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)
            && roundtrip_time_ms < timeout;) {
           recv_bytes = recv(ping_socket, answer_buffer, sizeof(answer_buffer), 0);
           clock_gettime(CLOCK_TYPE, &recv_time);
           roundtrip_time_ms = timespec_delta2milliseconds(&recv_time, &send_time);
       }
       if (recv_bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
           fail_errno("UDP ping could not recv from UDP socket");
       if (recv_bytes < sent_bytes) {  /*time-out elapsed: packet was lost */
           lost_count++;
           if (recv_bytes < 0)
               recv_bytes = 0;
           printf("\n ... received %zd bytes instead of %zd (lost count = %d); re-trying ...\n", recv_bytes, sent_bytes, lost_count);
           if (++re_try > MAXUDPRESEND) {
               printf(" ... giving-up!\n");
               fail("too many lost datagrams");
           }
           printf(" ... re-trying ...\n");
       }
   } while (sent_bytes != recv_bytes);

   return roundtrip_time_ms;
}

int prepare_udp_socket(char *pong_addr, char *pong_port)
{
   struct addrinfo gai_hints, *pong_addrinfo = NULL;
   int ping_socket;
   int gai_rv;

   /*** Specify the UDP sockets' options ***/
   memset(&gai_hints, 0, sizeof gai_hints);
/*** TO BE DONE START ***/
       gai_hints.ai_family = AF_INET;
       gai_hints.ai_socktype = SOCK_DGRAM;
       gai_hints.ai_protocol = 17;
/*** TO BE DONE END ***/

   if ((ping_socket = socket(gai_hints.ai_family, gai_hints.ai_socktype, gai_hints.ai_protocol)) == -1)
       fail_errno("UDP Ping could not get socket");
   /*** change socket behavior to NONBLOCKING ***/
/*** TO BE DONE START ***/
   if(fcntl(ping_socket, F_SETFD, O_RDWR|O_NONBLOCK)!=0) fail_errno("fcntl");
/*** TO BE DONE END ***/

   /*** call getaddrinfo() in order to get Pong Server address in binary form ***/
/*** TO BE DONE START ***/
   gai_rv = getaddrinfo(pong_addr, pong_port, &gai_hints, &pong_addrinfo);
   if(gai_rv<0) fail_errno("getaddrinfo");
/*** TO BE DONE END ***/

#ifdef DEBUG
   {
       char ipv4str[INET_ADDRSTRLEN];
       const char * const cp = inet_ntop(AF_INET, &(((struct sockaddr_in *)(pong_addrinfo-> ai_addr))->sin_addr), ipv4str, INET_ADDRSTRLEN);
       if (cp == NULL)
           printf(" ... inet_ntop() error!\n");
       else
           printf(" ... about to connect socket %d to IP address %s, port %hu\n",
                ping_socket, cp, ntohs(((struct sockaddr_in *)(pong_addrinfo->ai_addr))->sin_port));
   }
#endif

   /*** connect the ping_socket UDP socket with the server ***/
/*** TO BE DONE START ***/
   struct sockaddr_in *ipv4;
   ipv4 = (struct sockaddr_in *)pong_addrinfo->ai_addr;
   if(connect(ping_socket,  (struct sockaddr *) ipv4, sizeof(*ipv4))<0) fail_errno("connect");
/*** TO BE DONE END ***/

   freeaddrinfo(pong_addrinfo);
   return ping_socket;
}

int main(int argc, char *argv[])
{
   struct addrinfo gai_hints, *server_addrinfo;
   int ping_socket, ask_socket;;
   int msg_size, norep;
   int gai_rv;
   char ipstr[INET_ADDRSTRLEN];
   struct sockaddr_in *ipv4;
   char request[40], answer[10];
   ssize_t nr;
   int pong_port;

   if (argc < 4)
       fail("Incorrect parameters provided. Use: udp_ping PONG_ADDR PONG_PORT MESSAGE_SIZE [NO_REPEAT]\n");
   for (nr = 4, norep = REPEATS; nr < argc; nr++)
       if (*argv[nr] >= '1' && *argv[nr] <= '9')
           sscanf(argv[nr], "%d", &norep);
   if (norep < MINREPEATS)
       norep = MINREPEATS;
   else if (norep > MAXREPEATS)
       norep = MAXREPEATS;
   if (sscanf(argv[3], "%d", &msg_size) != 1 || msg_size < MINSIZE || msg_size > MAXUDPSIZE)
       fail("Wrong message size");

   /*** Specify TCP socket options ***/
   memset(&gai_hints, 0, sizeof gai_hints);
/*** TO BE DONE START ***/
   gai_hints.ai_family = AF_INET;
   gai_hints.ai_socktype = SOCK_STREAM;
/*** TO BE DONE END ***/

   /*** call getaddrinfo() in order to get Pong Server address in binary form ***/
/*** TO BE DONE START ***/
   if(getaddrinfo(argv[1], argv[2], &gai_hints, &server_addrinfo)!=0) fail_errno("getaddrinfo");
/*** TO BE DONE END ***/

   /*** Print address of the Pong server before trying to connect ***/
   ipv4 = (struct sockaddr_in *)server_addrinfo->ai_addr;
   printf("UDP Ping trying to connect to server %s (%s) on TCP port %s\n", argv[1], inet_ntop(AF_INET, &ipv4->sin_addr, ipstr, INET_ADDRSTRLEN), argv[2]);

   /*** create a new TCP socket and connect it with the server ***/
/*** TO BE DONE START ***/
   ask_socket = socket(AF_INET, SOCK_STREAM, 0);
   if(ask_socket<0) fail_errno("socket");
   if(connect(ask_socket, (struct sockaddr *) ipv4, sizeof(*ipv4))<0) fail_errno("connect");
/*** TO BE DONE END ***/

   freeaddrinfo(server_addrinfo);
   printf(" ... connected to Pong server: asking for %d repetitions of %d _bytes UDP messages\n", norep, msg_size);
   sprintf(request, "UDP %d %d\n", msg_size, norep);

   /*** Write the request on the TCP socket ***/
/** TO BE DONE START ***/
   if(write(ask_socket, request, strlen(request))<0) fail_errno("write");
/*** TO BE DONE END ***/

   nr = read(ask_socket, answer, sizeof(answer));
   if (nr < 0)
       fail_errno("UDP Ping could not receive answer from Pong server");
   if (nr==sizeof(answer))
       --nr;
   answer[nr] = 0;

   /*** Check if the answer is OK, and fail if it is not ***/
/*** TO BE DONE START ***/
   if(strncmp(answer, "OK ", 3)!=0) fail_errno("No answer from Pong :-(");
/*** TO BE DONE END ***/

   /*** else ***/
   sscanf(answer + 3, "%d\n", &pong_port);
   printf(" ... Pong server agreed to ping-pong using port %d :-)\n", pong_port);
   sprintf(answer, "%d", pong_port);
   shutdown(ask_socket, SHUT_RDWR);
   close(ask_socket);

   ping_socket = prepare_udp_socket(argv[1], answer);

   {
       char message[msg_size];
       memset(&message, 0, (size_t)msg_size);
       double ping_times[norep];
       struct timespec zero, resolution;
       int repeat;
       for (repeat = 0; repeat < norep; repeat++) {
           ping_times[repeat] = do_ping((size_t)msg_size, repeat + 1, message, ping_socket, UDP_TIMEOUT);
           printf("Round trip time was %6.3lf milliseconds in repetition %d\n", ping_times[repeat], repeat + 1);
       }
       memset((void *)(&zero), 0, sizeof(struct timespec));
       if (clock_getres(CLOCK_TYPE, &resolution) != 0)
           fail_errno("UDP Ping could not get timer resolution");
       print_statistics(stdout, "UDP Ping: ", norep, ping_times, msg_size, timespec_delta2milliseconds(&resolution, &zero));

   }

   close(ping_socket);
   exit(EXIT_SUCCESS);
}
...