This assignment is an enhancement of Assignment 3. Start by making a copy of your Assignment 3 files and rename them accordingly.
Implement the additional functionality for Assignment 4 by refactoring
The logic in your doLookupBooks()
and doServ()
funtions and add the logic for an iterative UDP server as shown in UNP 8.15.
It might be helpful to get a copy of the
udpcliserv/udpservselect01.c
file from the book's source
code and review the various UDP Client-Server notes on the main CSCI 631
web site.
Students in section 1 must implement their TCP client application according to the following usage statement:
Usage: prog4_client_tcp IP-number [port-number]
Students in section 1 must implement their UDP client application according to the following usage statement:
Usage: prog4_client_udp IP-number [port-number]
In both cases the IP number to connect to is required and the port number is optional. The default port number shall be 9880.
Students in section 1 will implement their server application according to the following usage statement:
Usage: prog4_server book-database-filename [port-number]
Note that both the TCP listener and the UDP server require a port
number. We will use the same port number for both. This is perfectly
acceptable and is quite common. Doing so is explicitly allowed because
TCP and UDP sockets have a different protocol value in their
socket()
calls. Thus they are differentiated by the operating
system based on protocol.
The first step of this assignment is to update the server in two phases. Phase-I is to refactor the book lookup logic so that it can be used for both the TCP and UDP server logic.
Note that the man page for fscanf() discusses the following conversion option:
Each conversion specification in format begins with either the charac- ter '%' or the character sequence "%n$" (see below for the distinction) followed by: * An optional 'a' character. This is used with string conversions, and relieves the caller of the need to allocate a corresponding buffer to hold the input: instead, scanf() allocates a buffer of sufficient size, and assigns the address of this buffer to the corresponding pointer argument, which should be a pointer to a char * variable (this variable does not need to be initialized before the call). The caller should subsequently free(3) this buffer when it is no longer required. This is a GNU exten- sion; C99 employs the 'a' character as a conversion specifier (and it can also be used as such in the GNU implementation).
What this means is that fscanf(), on an OS that uses the GNU C library such
as Linux, provides a way in which you can let fscanf()
perform a
malloc()
and return
the space needed to contain the strings you are extracting from the input
lines. This means that you can not run into a buffer overrun problem!
Here is an example of how to use fscanf()
with %m to allocate properly
sized buffers to the title and author fields in the 'database'. Note that
since they are malloc()'d, they have to be free()'d. Else your program will
leak memeory. (Note also that I used %m rather than %a. This is because of
the notes at the end of the man page for fscanf().)
You are free to use an alternate approach to scanning the book database. As before, you can use fixed-sized buffers to hold the author and title. This assignment is not primairly concerned with reading the file.
static int doLookupBook(const char *bookdb, const char *title, char *author, size_t authorLen) { fprintf(stderr, "Looking up: '%s'\n", title); // open bookdb FILE *db = fopen(bookdb, "r"); // look it up int match = -1; char *ba; char *bt; while(match && fscanf(db, " %m[^:]:%m[^:]:", &bt, &ba) == 2) { fprintf(stderr, "checking '%s', '%s'\n", bt, ba); if ((match = strcmp(bt, title)) == 0) { strncpy(author, ba, authorLen-1); author[authorLen-1] = '\0'; } free(ba); free(bt); } fclose(db); return(!match); }
To use this new function, you must refactor doLookupBooks()
into
doLookupBooksTCP()
and doLookupBook()
.
Then verify that your program
still works the same as it did before (it should, as you would not have
done anything other than to change its structure to an equivelant form.
Phase-II. Incorporate the select()
and UDP server logic from
UNP 8.15.
Your UDP server logic should read a packet that contains the title (and an optional sequence number) and respond with the author (and an optional sequence number). The title packet MUST not be required to include a null-character string terminator, nor SHOULD it contain trailing whitespace.
This is the same as prog3_client. Just rename prog3_client.c
to prog4_client_tcp.c
Make sure that your prog4_client_tcp
terminates itself when
you press ^D
and send it an EOF condition.
Use the code in UNP Figure 8.7 and 8.8 as a guide for how to write your
UDP client and put your main()
in prog4_client_udp.c
and a UDP query function to query.c
and query.h
with the following signature:
int doQueryUDP(const char *ip, uint16_t port, const char *title, char *author, size_t authorlen);
As with the TCP client, trim off any whitespace from the keyboard input
before sending
the title to your doQueryUDP()
function. The server must receive a
packet with EXACTLY the number of characters as are required for the title.
For example the title 1984
must arrive at your server such that the
last byte is the '4' character.
It must NOT be terminated by a null character. It must not contain a newline.
Verify this by printing the length AND the value received on the server with
quotes around it to verify.
Then remove such debuging output when you know it is correct.
Notice that there is no value in using Readline()
in your UDP
client app because it does not read lines... it reads datagrams.
There is no reason to trim whitespace off the UDP responses from the server.
You will have written a proper server as discussed above that returns the
author with no triling whitespace. Simply add a null terminating character
to the response datagram that your client receives from your server and
return it from doQueryUDP()
.
If you did Assignment 3 properly, you should be able to copy your
prog3_client.c
to prog4_client_udp.c
and do
nothing more than change doQuery
to doQueryUDP
In the first version of your prog4_client_udp
, just get it to
work. By that I mean just call sendto()
and
recvfrom()
without worrying about timeouts.
Once you have that working you can optionally add a select(2) that provides a timeout so that your client will not get stuck if the server response never arrives.
Make sure that your prog4_client_udp
terminates itself when
you press ^D
.
In order to verify that your server can properly handle both UDP and TCP clients at the same time you should use multiple instances of both your TCP and UDP clients that are connected/querying your server at the same time.
Here is the Makefile that MUST be used by students in section 1 to
compile the application code for this assignment. Note that this
Makefile will produce client application executables named
prog4_client_tcp
, prog4_client_udp
and a server application
executable named prog4_server
.
To hand in this assignment, execute the following command on tiger:
mail_prog.631 prog4_client_tcp.c prog4_client_udp.c wrapper.c wrapper.h in_out.c in_out.h query.c query.h prog4_server.c serv.c serv.h
Last modified: 2018-05-28 11:04:55 CDT
Copyright © 2025 John Winans All rights reserved.