Project05 - Chat app
Due:
Wed, May 4, 2022 at 11:59 PM
to Github Classroom Assignment
Requirements
- You will develop a chat app which runs on the CSLabs Linux machines
- Your repo must contain a
Makefile
which generates an executable calledlab05
- Your app will broadcast your online presence periodically
- Your app will use non-blocking I/O to send and receive chat messages from other users on the network
- Network requirements
- As in lab07, you will broadcast presence to the UDP broadcast address
10.10.13.255
- Your app will use UDP port
8221
for presence and your assigned TCP port for 1:1 chat messages
- As in lab07, you will broadcast presence to the UDP broadcast address
- Behavior requirements
- You must use your USF username (e.g. phpeterson) for presence and chat messages – no pseudonyms
- You must use the network in accordance with the USF’s Technology Resources Appropriate Use Policy
Divide and Conquer
- You can reuse your client code from lab07 which broadcasts your presence using the format:
online phpeterson 8382
andoffline phpeterson 8382
- You can reuse your server code from lab07 and incorporate it into
project05
to accept presence messages - When you know who’s online, you can create a data structure (array or linked list) which contains the username and IP address or hostname
- You can develop new code to use TCP sockets to send and receive chat messages
- You can accept user input
@phpeterson: do you have OH today?
onstdin
usingpoll()
as we will discuss in lecture.
Boundary Conditions
- You may assume that there are no more than 64 users online at a time
- You may assume that chat messages are no more than 128 characters long, and are NUL-terminated C strings
Rubric
- 20 pts: Presence: periodic online broadcasts, offline when your app exits
- 40 pts: Two-way 1:1 chat
- 20 pts: Interactive grading questions
- 10 pts: Error handling and memory management
- 10 pts: Neatness
Extra credit
- 2 pts: Add support for channels, e.g.
#sushi-lovers: lunch at Amiti's?
. Demonstrate this using multiple instances of yourproject05
executable on multiple lab hosts. You may assume that all of your instances (phpeterson-1
,phpeterson-2
, …) are members of the#sushi-lovers
channel
Implementation Guide
Non-Blocking I/O
- Your program needs to process network connections and user input in a time-sliced way. You may want to use
scanf()
to get user input, but that blocks, which means network connections wouldn’t be processed. - Your program should use
poll()
to do time-slicing between these activitiesstruct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- You should write a loop which looks roughly like this:
struct pollfd fds[64]; int nfds = ...; while (!done) { int num_ready = poll(fds, nfds, 100); if (num_ready == ...) /* do "server" things */ }
- You will use a
pollfd
for your UDP broadcast socket on port 8221 and for your TCP listener on your assigned port. - After you have created your
pollfd
structs, you can callpoll()
in awhile(!done)
loop poll()
returns the number ofpollfd
structs which have requested flags (POLLIN
) set. You should loop over yourfdpoll
array and process the file descriptors which have data available (STDIN_FILENO, or UDP broadcast, or TCP chat)
Polling STDIN_FILENO
for user input
- Since
stdin
is just a file with a file descriptor (STDIN_FILENO
), you can read keyboard input by using apollfd
forstdin
and wait forPOLLIN
events on that FD poll()
will return bits and pieces of user input typed in between timeouts- You can read the
pollfd.fd
usinggetchar()
or other functions - You should accumulate (
strcat()
?) the bits and pieces you read in a string - When the user types a
'\n'
you should write the string to the TCP socket for the recipient (client side, like the TCP test app) - You should choose keyboard sequence to use to exit your loop. Perhaps
q
or use CTRL-D, which causesgetchar()
to returnEOF
(or -1)
UDP Broadcast
- As in lab07, you will use
socket()
,setsockopt()
and UDPsendto()
to broadcast your presence to the labs subnet (10.10.13.255
) which includes all computers on the subnet listening to port8221
(as in the given UDP client) - Your project05 program will add the ability to receive new UDP messages, using the code from your lab07 server (similar to the given
lab-udp
server in the inclass repo)- Setup the connection using the hints,
getaddrinfo()
, andbind()
- When your UDP file descriptor is ready to read,
poll()
will put the flagPOLLIN
in therevents
of thepollfd
- When that happens, you can use UDP
recvfrom()
to get the data waiting to be read (as in the given UDP server)
- Setup the connection using the hints,
- Protocol: The UDP packet should contain
online yourname yourport
. You can create this string withsnprintf()
and parse an incoming presence broadcast withsnscanf()
TCP Setup
- You will use TCP (stream) sockets to do 1:1 communication with other users.
- For
socket()
the domain is stillPF_INET
, the type isSOCK_STREAM
(rather than DATAGRAM) and the protocol isIPPROTO_TCP
(rather than UDP) - You should set up a TCP listener using hints,
getaddrinfo()
,socket()
,bind()
andlisten()
. The socket should haveSO_REUSEADDR
- The socket needs to be non-blocking, which you can set up like this:
int enable = 1; ioctl(fd, FIONBIO, (char*) &enable);
- You should add the TCP file descriptor to a
pollfd
in your array ofpollfd
so thepoll()
loop can notify you when someone is trying to connect.
Reading from the TCP socket
- When the TCP listener
pollfd
hasrevents & POLLIN
you shouldaccept()
the connection, which creates a new file descriptor for the chat between you and the sending person - That new file descriptor should be added to your list of
pollfd
so you can use it to send and receive messages with that user. One new FD will be created per user you chat with.
Writing to the TCP socket
- If the user types
@phpeterson: office hours?
you should lookupphpeterson
in your list of (user, host, port) - You should use hints and
getaddrinfo()
using the user’s hostname (e.g.vlab21.cs.usfca.edu
) to create a socket, and thenconnect()
to the socket:connect(fd, res->ai_addr, res->ai_addrlen);)
- When the socket has been connected, you can use TCP
send()
to transmit the user’s message to the socket:int sent = send(fd, msg, len, 0);
Keeping Track of Users
- In order to keep track of who is online, you may wish to build a list of users using a
struct
something like thisstruct user { char status[BUF_SIZE]; /* online or offline? */ char name[BUF_SIZE]; /* user name */ char port[BUF_SIZE]; /* TCP listener port */ char host[NI_MAXHOST]; /* host name */ }
- When you receive a presence message via UDP broadcast, you have the status, name, and port ready to
snscanf()
- You can get the hostname using
getnameinfo()
as the given UDP server does.
Quitting the program
- If you catch
CTRL-D
(also known asEOF
) when pollingSTDIN_FILENO
, that can stop thepoll()
loop - You can come up with an alternative, but CTRL-C does not shut down cleanly so you should not use that.
- When your program exits cleanly, you can broadcast offline presence and
close()
all of thepollfd
array.