/* udpserv.c */ /* A udp server which serves by consuming data from a remote */ /* client and informing the client regarding the rate of */ /* packet loss. */ #include #include #include #include #include #include #include #include #include #include "udpcs.h" char buf[1024]; struct hostent *hp; struct hostent *gethostbyname(); struct sockaddr_in name; struct sockaddr_in from; struct sockaddr_in sname; int namelen; struct timeval time1; struct timeval time2; static struct timezone dummy = {0,0}; apphdr_t conn_resp; apphdr_t close_resp; statresp_t stat_resp; main(argc, argv) int argc; char *argv[]; { int i; unsigned char c; int sock; int netaddr; int status; apphdr_t *apphdr; int connid = 0; /* Create datagram socket */ sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { printf("Socket create failed \n"); exit(1); } /* Fill in protocol family and port # from command line then */ /* bind the socket to the specified address. */ name.sin_family = AF_INET; name.sin_port = htons(atoi(argv[1])); status = bind(sock, (struct sockaddr *)&name, sizeof(name)); printf("Bind status = %d \n", status); if (status < 0) exit(1); /* Setup input header pointer and connection response buffer */ /* Header structure is defined in udpcs.h */ apphdr = (apphdr_t *)buf; conn_resp.magic = htonl(UDPCS_MAGIC); conn_resp.seq = 0; /* Not used */ conn_resp.ptype = htonl(PKT_CONN_ACK); close_resp.magic = htonl(UDPCS_MAGIC); close_resp.seq = 0; /* Not used */ close_resp.ptype = htonl(PKT_CLOSE_ACK); stat_resp.apphdr.magic = htonl(UDPCS_MAGIC); stat_resp.apphdr.seq = 0; /* Not used */ stat_resp.apphdr.ptype = htonl(PKT_STAT_RESP); /* Wait for someone to send me a connection request */ while (1) { namelen = sizeof(struct sockaddr_in); status = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&sname, &namelen); printf("status = %d \n", status); printf("Sender net address = "); for (i = 0; i < 4; i++) { c = *((char *)&sname.sin_addr + i); printf(" %2x", c); } printf("\n"); printf("Sender port address = %x \n", ntohs(sname.sin_port)); /* Ensure that length, magic #, and type are correct */ if (status != sizeof(apphdr_t)) { printf("bad message length %d \n", status); continue; } if (ntohl(apphdr->magic) != UDPCS_MAGIC) { printf("bad magic number %x \n", ntohl(apphdr->magic)); continue; } if (ntohl(apphdr->ptype) != PKT_CONN_REQ) { printf("bad packet type %x \n", ntohl(apphdr->ptype)); continue; } /* If we get here we appear to have a valid connection */ /* request... Create a server to handle it. */ /* Note that with UDP we must explicitly create a new */ /* socket and connect it to the remote client */ status = fork(); if (status == 0) { int newsock; newsock = socket(PF_INET, SOCK_DGRAM, 0); status = connect(newsock, &sname, namelen); /* Send a connect ack using the new socket.. The client */ /* will direct the data to be consumed to this new */ /* port address. */ write(newsock, &conn_resp, sizeof(apphdr_t)); do_serv(newsock, connid, &sname); } connid++; } } /**/ /* This thread is used to recover from cases in which the client */ /* dies before sending the close or the close gets lost.. */ /* A client who fails to send for 10 seconds is assumed dead */ static int alive; void *keepalive(void *p) { while (1) { alive = 0; sleep(10); if (alive == 0) kill(getpid(), SIGKILL); } } /**/ /* This is the function that actually performs the service */ #define BUF_SIZE 2048 int do_serv( int sock, int connid, struct sockaddr_in *sname) { int len; int seq; int nextseq = 0; int drops = 0; int recvs = 0; double count = 0; double tu1; double tu2; pthread_t tid1; pthread_attr_t attr; unsigned char *buf = (unsigned char *)malloc(BUF_SIZE); apphdr_t *apphdr; /* Create the thread that will periodically check to */ /* to see if data is still being received.. */ pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); fprintf(stderr, "Creating threads \n"); pthread_create(&tid1, &attr, keepalive, (void *)0); /* This is the main receive loop.. One trip through for every */ /* packet received.. Exit is from the middle on receipt of */ /* a close request */ apphdr = (apphdr_t *)buf; while (1) { len = read(sock, buf, BUF_SIZE); /* Make sure this appears to be a valid packet and not some */ /* traceroute! */ if (len < sizeof(apphdr_t)) { printf("Length error %d \n", len); continue; } if (ntohl(apphdr->magic) != UDPCS_MAGIC) { printf("bad magic number %x \n", ntohl(apphdr->magic)); continue; } /* When a close is received compute drop% and bit rate and quit */ if (ntohl(apphdr->ptype) == PKT_CLOSE_REQ) { printf("Close requested.. recvs = %d drops = %d rate = %6.1f \n", recvs, drops, 100.0 * drops / (drops + recvs)); tu1 = 1000000.0 * time1.tv_sec; tu1 += time1.tv_usec; tu2 = 1000000.0 * time2.tv_sec; tu2 += time2.tv_usec; printf("Bit rate was %12.0f \n", (double)8000000 * count / (tu2 - tu1)); write(sock, &close_resp, sizeof(apphdr_t)); sleep(1); write(sock, &close_resp, sizeof(apphdr_t)); exit(0); } /* Only PKT_DATA and PKT_DATA_POLL remain valid */ if ((ntohl(apphdr->ptype) & ~1) != PKT_DATA) { printf("bad packet type %x \n", ntohl(apphdr->magic)); continue; } /* If we get here this must be a data packet */ count += len; /* Track number of bytes consumed */ alive = 1; /* Flag still alive */ /* If this is the first packet remember start time */ /* else update end time. */ if (recvs == 0) gettimeofday(&time1, &dummy); else gettimeofday(&time2, &dummy); recvs += 1; /* Check for some client builder who forgets to increment */ /* sequence numbers or transmit them in network byte order */ seq = ntohl(apphdr->seq); fprintf(stderr, "%8d\n", seq); if (seq < nextseq) { printf("bad sequence number %d last was %d \n", seq, nextseq); continue; } /* Adjust drop count */ drops += seq - nextseq; nextseq = seq + 1; /* If this is a poll request provide drop status */ if (ntohl(apphdr->ptype) == PKT_DATA_POLL) { stat_resp.thisseq = htonl(seq); stat_resp.drops = htonl(drops); write(sock, &stat_resp, sizeof(statresp_t)); } } }