/* * udpserver.c - A simple UDP echo server * usage: udpserver * * Based on: https://www.cs.cmu.edu/afs/cs/academic/class/15213-f99/www/class26/udpserver.c * (which was probably based on W.R. Stevens' "Unix Network Programming" original). * * Connect to this on localhost with, e.g., "nc -i 127.0.0.1 8888" and type some messages; * each message should generate a packet to the server, and its response packet in return. * * What happens if you pipe in a string longer than the single UDP packet's payload length? * E.g.: $ perl -e 'print "A"x2500;' | nc -u tahoe.cs.dartmouth.edu 8888 OR * $ perl -e 'print "B"x1000 . "A"x1500;' | nc -u tahoe.cs.dartmouth.edu 8888 * (assuming your server runs on tahoe.cs.dartmouth.edu port 8888) */ #include #include #include #include #include #include #include #include #include #define BUFSIZE 1024 void lookup_and_log_peer(struct sockaddr_in *cliaddr); /* * error - wrapper for perror */ void error(char *msg) { perror(msg); exit(1); } int main(int argc, char **argv) { int sockfd; /* socket */ int portno; /* port to listen on */ int clientlen; /* byte size of client's address */ struct sockaddr_in serveraddr; /* server's addr */ struct sockaddr_in clientaddr; /* client addr */ char buf[BUFSIZE]; /* message buf */ int optval; /* flag value for setsockopt */ int n; /* message byte size */ /* * check command line arguments */ if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); } portno = atoi(argv[1]); /* * socket: create the parent socket */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) error("ERROR opening socket"); /* setsockopt: Handy debugging trick that lets * us rerun the server immediately after we kill it; * otherwise we have to wait about 20 secs. * Eliminates "ERROR on binding: Address already in use" error. */ optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int)); /* * build the server's Internet address */ bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any interface/address this machine has. serveraddr.sin_port = htons((unsigned short)portno); /* * bind: associate the parent socket with a port * (Note that creating the socket had nothing about any port or IP. * Creation in sockets is separate from configuration). */ if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) error("ERROR on binding"); /* * main loop: wait for a datagram, then echo it */ clientlen = sizeof(clientaddr); while (1) { /* * NOTE: No listen(). UDP does not queue requests, no has any special * state in which it acknowledges packets without sending any * data of its own (unlike SYN+ACK for a TCP listening socket). * UDP sockets are "born" ready to write/send, and, once bound, are * ready to read/receive. Simple. * * But there is a price from this simplicity: the reading/receiving * call such as revcfrom() must take a pointer to an address struct * to fill in with the address of the connecting peer. Otherwise * we would not know who connected to us, i.e., who to send back to. * Note also that we pass a pointer to a length of that struct, * so that we may know how many bytes were actually filled in * (recall that sockaddr structs were designed to accommodate all * kinds of address schemes, not just the Internet's AF_INET!). * * But wait, there's more! Since there's no queue, what happens if * messages come too fast, say, while thise code is busy sending * a response back? See top of file for some testing ideas. */ /* * recvfrom: receive a UDP datagram from a client */ bzero(buf, BUFSIZE); n = recvfrom(sockfd, buf, BUFSIZE, 0, (struct sockaddr *) &clientaddr, (socklen_t *)&clientlen); if (n < 0) error("ERROR in recvfrom"); lookup_and_log_peer( &clientaddr ); printf("server received %lu/%d bytes: %s\n", strlen(buf), n, buf); /* * sendto: echo the input back to the client */ n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *) &clientaddr, clientlen); if (n < 0) error("ERROR in sendto"); } } void lookup_and_log_peer(struct sockaddr_in *sinaddr) { struct hostent *hostp; /* client host info */ char *hostaddrp; /* dotted decimal host addr string */ /* * Note: gethostbyaddr allocates or reuses its with hostent buffer! * inet_ntoa also allocates or reuses its own string buffer! */ /* * gethostbyaddr: determine who sent the datagram */ hostp = gethostbyaddr((const char *)&(sinaddr->sin_addr.s_addr), sizeof(sinaddr->sin_addr.s_addr), AF_INET); if (hostp == NULL) error("ERROR on gethostbyaddr"); hostaddrp = inet_ntoa(sinaddr->sin_addr); if (hostaddrp == NULL) error("ERROR on inet_ntoa\n"); printf("server received datagram from %s (%s)\n", hostp->h_name, hostaddrp); }