我必须使用DGRM套接字实现UDP客户端,同时仅修改/***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,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],&msg_size) != 1 || msg_size < MINSIZE || msg_size > MAXUDPSIZE)
fail("Wrong message size");
/*** Specify TCP socket options ***/
memset(&gai_hints,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],&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,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",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,&pong_port);
printf(" ... Pong server agreed to ping-pong using port %d :-)\n",pong_port);
sprintf(answer,pong_port);
shutdown(ask_socket,SHUT_RDWR);
close(ask_socket);
ping_socket = prepare_udp_socket(argv[1],answer);
{
char message[msg_size];
memset(&message,(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,UDP_TIMEOUT);
printf("Round trip time was %6.3lf milliseconds in repetition %d\n",ping_times[repeat],repeat + 1);
}
memset((void *)(&zero),sizeof(struct timespec));
if (clock_getres(CLOCK_TYPE,&resolution) != 0)
fail_errno("UDP Ping could not get timer resolution");
print_statistics(stdout,"UDP Ping: ",ping_times,timespec_delta2milliseconds(&resolution,&zero));
}
close(ping_socket);
exit(EXIT_SUCCESS);
}