|
| 1 | +--- |
| 2 | +layout: doc |
| 3 | +title: "Charming Chatroom" |
| 4 | +--- |
| 5 | + |
| 6 | +## Learning Objectives |
| 7 | + |
| 8 | +* Networking Components |
| 9 | +* Building working server code |
| 10 | +* Building working client code |
| 11 | +* Network error handling |
| 12 | +* Read/Write error handling |
| 13 | + |
| 14 | + |
| 15 | +## Goal |
| 16 | + |
| 17 | +The goal of this lab is to help you understand networking components. You will accomplish this by writing a real chatroom program. You are going to write a client which can send/receive data to/from server, a server which can receive data from multiple clients and broadcast these messages to each of the clients, and read/write functions than handle the failures of read and write. The files you must modify are |
| 18 | + |
| 19 | +* client.c |
| 20 | +* server.c |
| 21 | +* utils.c |
| 22 | + |
| 23 | +The files `client.c` and `server.c` provide an outline of what you are expected to do via references to questions in questions.txt. For example, if you see /*QUESTION 1*/ then question 1 in `questions.txt` should help you understand what to do when filling that part of the code. |
| 24 | +**So be sure to answer the questions in `questions.txt` to begin with as these will help you get started!** |
| 25 | + |
| 26 | + |
| 27 | +## Client |
| 28 | + |
| 29 | +The file `chat_window.c` is the control center for setting up the ncurses windows (that's a thing). You do not need to worry about this, but feel free to look at it if you are interested. |
| 30 | + |
| 31 | +The client executable will accept up to four arguments: |
| 32 | +``` |
| 33 | +./client <host> <port> <username> [filename] |
| 34 | +``` |
| 35 | + |
| 36 | +* `host` - The address the client should connect to. |
| 37 | +* `port` - The port number to connect to the host on. |
| 38 | +* `username` - The name you want to be displayed in the chatroom. |
| 39 | +* `filename` - Optional argument - will disable ncurses and write bytes received from the server to the output file. |
| 40 | + |
| 41 | +The client performs two main functions: (1) writes user input to the server and (2) writes bytes from server to user. We have handled the overall logic of reading from user, writing to server, reading from server, and writing to user. Your job is to set up the client and connect it to the server. |
| 42 | + |
| 43 | +Implement the provided function to use a TCP IPv4 connection and connect to the host at the given port. A signal interrupt will be sent to the client as a flag to tell your client to exit. To be precise, when your client program receives a `SIGINT` it should free memory, close sockets, and gracefully exit the program. |
| 44 | + |
| 45 | +**Notice** the writing and reading to the server use `write_all_to_socket()` and `read_all_from_socket()`. You will have to implement these functions to handle the failures of read/write calls, but more on that later. |
| 46 | + |
| 47 | +The figure below gives you an idea about how the client side architecture looks like:  |
| 48 | + |
| 49 | +So to sum up, your job in the client program is: |
| 50 | + |
| 51 | +* Implement the running client and closing client functions |
| 52 | +* Set up the network connection (TCP + IPv4). |
| 53 | +* Launch threads to read from the server. |
| 54 | +* Launch threads to write to server. |
| 55 | +* Free memory you allocate. |
| 56 | + |
| 57 | +**Note:** You do not need to modify any of the code in `client.c` except for the function `connect_to_server()` and `close_server_connection()` in order to get the client successfully working. However, you *may* modify any of the other code if you want, but **be careful**. |
| 58 | + |
| 59 | + |
| 60 | +## Server |
| 61 | + |
| 62 | +``` |
| 63 | +./server <port> |
| 64 | +``` |
| 65 | +* port - The port number to accept connections on. |
| 66 | + |
| 67 | +Similar to `client.c`, a lot of the functionality in `server.c` has been implemented for you. Your job is to set up the server to use TCP IPv4 with reusable ports and gracefully close the server when `SIGINT` is received. |
| 68 | + The figure below illustrates how a message propagates through the system:  |
| 69 | + |
| 70 | +To sum up, you have to: |
| 71 | + |
| 72 | +* Implement `run_server()` and `close_server()` (the signal handler for SIGINT) |
| 73 | +* Set up connections (TCP & IPv4). |
| 74 | +* (There is a giant while-loop - you need to do something in it) |
| 75 | + |
| 76 | +Here is the overall client-server architecture: |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +## Read/Write Failures |
| 81 | + |
| 82 | +Read and Write calls (general `read`/`write` - this extends to `recv`, `send`, etc.) can fail to send/receive all bytes. Here is the pseudocode for read all to socket. |
| 83 | + |
| 84 | +``` |
| 85 | +while number of bytes is not the number needed: |
| 86 | + return_code = read bytes from socket |
| 87 | + if return_code = 0: |
| 88 | + return bytes read |
| 89 | + else if return_code > 0: |
| 90 | + add return_code bytes to counter |
| 91 | + else if return_code == -1 and error was interrupted: |
| 92 | + try again |
| 93 | + else: |
| 94 | + return bytes read |
| 95 | +return bytes read |
| 96 | +``` |
| 97 | + |
| 98 | + |
| 99 | +In `utils.c/h` we have declared the functions `read_all_from_socket` and `write_all_to_socket`. You need to implement these functions to read/write from/to a socket and handle the failures of read/write (defined above). You should look at `utils.h` for detailed info on what your functions should do. |
| 100 | + |
| 101 | +Messages in our server/client will be exchanged in the following format: |
| 102 | + |
| 103 | +``` |
| 104 | +<message_size><message> |
| 105 | +informally: 0x0000000C"hello world\n" |
| 106 | +``` |
| 107 | +where the **first 4 bytes** of the message indicate the size of the message in bytes. |
| 108 | +We use network-to-host byte-order (ntohl) and host-to-network byte-order (htonl) for this. |
| 109 | +We've given you `get_message_size()`, you **must** implement `write_message_size()`. As we will be testing your server and client separately, and because our server and client expect this setup, be sure this works; it is like our "protocol". |
| 110 | +Please see how `get_message_size()` it is done in `utils.c` |
| 111 | + |
| 112 | +In `user_hooks.c` we have given an example of how to emulate a read failure (feel free to modify this file by adding a write failure call). You can include `user_hooks.h` in your utils/server/client code to test your error handling. |
| 113 | +**BE SURE NOT TO LEAVE ANYTHING RELATING TO `user_hooks` IN YOUR FILES. THEY ARE THERE FOR TESTING, THE AG WILL NOT COMPILE THEM** |
| 114 | + |
| 115 | +## Error Checking |
| 116 | + |
| 117 | +### Networks Break! |
| 118 | + |
| 119 | +Many things can go wrong when working with networks. Be sure to do plenty of error checking! If anything fails, print the error message and **exit(1)**. For each networking function call (except one of them) that you will be using, use **perror(NULL)**. For the one that you cannot use `perror`, use the man pages to learn how to print the error. You must figure out which function does not set `errno` upon failure and thus you cannot use `perror`. |
| 120 | + |
| 121 | + |
| 122 | +## Testing |
| 123 | +You should implement tests for testing functionality of client and server side code separately. That way you don't worry about client-server interactions. |
| 124 | +You can then consider cases where a message is written to one client, is sent to the server and is then broadcasted to all the other clients running. To test such a code path, you start a test that writes to one of the clients **c** and then verifies whether all the clients have received the message that was provided as input to **c**. |
| 125 | + |
| 126 | +Note, you are writing a server that could potentially run for a long time. This is exactly a scenario where memory leaks can be very costly and cause your server to crash. So ensure there are no memory leaks in your implementation. |
| 127 | + |
| 128 | +Otherwise, we will leave it open ended to let you come up with other interesting test cases. |
| 129 | + |
| 130 | +### We provided you with a Makefile. |
| 131 | + |
| 132 | +You can run the client and server as follows: |
| 133 | +``` |
| 134 | +./client <address> <port> <username> [filename] |
| 135 | +./server <port> |
| 136 | +``` |
| 137 | + |
| 138 | + |
| 139 | +Test your server and client with a friend! On your VMs you can connect to each others' machines. Just ask them what their machine number is! |
| 140 | + |
| 141 | + |
| 142 | +``` |
| 143 | + ./server 12345 |
| 144 | +Waiting for connection... |
| 145 | +Connection made: client_fd=4 |
| 146 | +^CEnding Server |
| 147 | +``` |
| 148 | + |
| 149 | +``` |
| 150 | + ./client {{site.semester}}-cs241-XYZ.cs.illinois.edu 12345 steve |
| 151 | +steve: hello there! |
| 152 | +alec: sending bytes |
| 153 | +^CClosing Client |
| 154 | +``` |
| 155 | +Notice the **XYZ**, that is the machine number you will use to connect to the server (the person hosting's machine) |
| 156 | +In the above "terminals", there are things like "Waiting for connection..." and "Ending Server": do not worry about having to do this. It is simply flavor text printed to the window. Feel free to do that; we will only test the bytes that are sent and received. |
| 157 | +**Because it's particularly important for this lab, we want to reiterate that you should not have any memory leaks :)** |
| 158 | + |
| 159 | +## Grading |
| 160 | + |
| 161 | +* Client must function as specified |
| 162 | +* Server must function as specified |
| 163 | +* Implementation of `read_all_from_socket` and `write_all_from_socket` to handle failures |
| 164 | +* Errors must be handled as specified |
| 165 | +* No memory leaks |
| 166 | + |
| 167 | +Once again, you do not need to modify any of the provided code in order to complete the lab, but you *can* if you want (however, be very careful; we are not responsible for your changes ) |
| 168 | + |
| 169 | + |
| 170 | +## Interesting Things |
| 171 | + |
| 172 | +We are using `pthread_cancel`, `pthread_detach`, `pthread_cleanup`, and `ncurses`. It is not necessary to know about these (actually *cancel* might have been on one of your quizzes), so don't let them distract/confuse you. However, if you are interested, definitely read up on them. |
| 173 | + |
| 174 | +In the Makefile, we have `-I./includes`. This lets you do `#include "something.h"` instead of `#include "./includes/something.h"` |
| 175 | + |
| 176 | +Also `#pragma once` in the .h files |
0 commit comments