CSCI-4220-01
Network Programming
Mon/Thurs 10:00-11:50am
Ricketts 211
Executive Officer
Department of Computer Science
Rensselaer Polytechnic Institute
All projects are detailed below.
Each project is to be completed individually (or as specified in the assignment description).
Unless otherwise noted, assignments are due by 11:59pm on the specified due date and are to be submitted via RPI LMS.
For C programs, use gcc to compile your code and specify -Wall to ensure all compiler warnings are addressed before submitting your assignment (points will be deducted for warnings!).
For C++ programs, use g++ to compile your code and specify -Wall to ensure all compiler warnings are addressed before submitting your assignment.
For Java programs, use javac to compile your code and java to execute it.
For any other language, please confirm language, version, libraries, etc. with me first.
| My Office: | Lally 209 |
|---|---|
| My Office Hours: | Mon 2:00-4:00pm, Tues 12:00-2:00pm, Fri 12:00-1:00pm |
| My Email: | goldschmidt@gmail.com |
| TA: | Abhirami Baskaran |
|---|---|
| Office Hours: |
Wed 2:00-6:00pm (in AE 217) |
| Email: | abhirami.baskaran@gmail.com |
| TA: | Aleksey Levy |
|---|---|
| Office Hours: |
Wed 12:00-1:50pm, Thurs 3:30-4:30pm (in AE 217) |
| Email: | levya@rpi.edu |
With multiple flavors of Unix available, please be sure to use Ubuntu for homeworks and projects detailed below. Using version 11.04 or higher is required.
Ubuntu is a free download (click here for Ubuntu download site). Also note that the VCC has Ubuntu CDs you can borrow.
For installations on Windows, you do not need to partition your hard drive. You can install Wubi instead (click here for Ubuntu Wubi site).
Due Monday 2/13
Our first assignment is to be completed individually. Do not share code or review anyone else's code. Work on this project is to be your own.
Submit your project via RPI LMS as a single compressed and zipped file (using tar and gzip); include only source code and documentation files.
To package up your submission, use tar and gzip. More specifically, create a compressed tar file using your RCS userid, as in goldsd.tar.gz, that contains your source files (e.g. main.c and file2.c); include a readme.txt file only if necessary. Here's an example:
bash$ tar cvf goldsd.tar main.c file2.c readme.txt main.c file2.c readme.txt bash$ gzip -9 goldsd.tar
Be sure to comment your code and include your name at the top of each file submitted.
CLARIFICATIONS AND HINTS:
(1) Be sure to name your project submission by using your RCS userid,
as in goldsd.tar.gz (i.e. <RCS-userid>.tar.gz).
(2) There is no need to use IPC system calls such as pipe(), fork(), and
so on; this project is purely simulation (though test code for layer 1
could easily be replaced with production code that communicates via
a pipe or socket to another layer 1 via the peer-to-peer protocol).
(3) You can hardcode the maximum chunk size at 16 bytes, though using a
preprocessor directive (e.g. #define MAX_CHUNK_SIZE 16) would be best.
(4) Remember that each layer is only allowed to use the adjacent layer!
Therefore, if a 32-byte message is to be sent and layer 4 adds a
checksum of 1 byte, the message will be sent via layer 2 in 3 chunks.
(5) Assume layer 1 will always transmit the correct NUMBER of bytes, but
do NOT assume that all the bytes are transmitted correctly.
(6) For layer 4, use a checksum of 1 byte (and there's no need to do any
additional error checking or retransmissions).
(7) If an error occurs at any layer, propagate that error (-1) back up
the protocol stack (again, no requirement to retransmit scrambled
messages/chunks/bytes at all).
(8) Be sure that if you have three command-line arguments that you
check the lengths of the arguments argv[1] and argv[2].
(9) Related to (8), be careful with your choice of delimiters in any
message you send. Do NOT assume that a space character (or other
"unusual" or "atypical" characters) will be safe as a delimiter.
For example, this is valid:
bash$ ./hw1 "shirley ann" jackson 660000001 =================>
--argv[1]-- argv[2] -argv[3]-
(10) To help your debugging, use #ifdef DEBUG_MODE and the corresponding
compilation line:
bash$ gcc -Wall -DDEBUG_MODE ...
(11) You can assume that the command-line argument corresponding to the RIN
is a valid integer. Use atoi() to convert it to an int data type (and
check out the man page for atoi() to understand its behavior).
Summary:
In this assignment, you'll develop C code to support a half-duplex layered communication system.
The interface protocols for each of the layers are provided to you as C function prototypes.
Your job is to implement these layers, and part of this involves the development of the peer-to-peer protocols.
You're responsible for implementing the C functions that provide layers 2, 3, 4, and 5 (which are described below). You do not need to provide a layer 1 implementation. A sample layer 1 implementation, along with a sample main program, is provided below. When testing your code, however, we will use our own layer 1 implementation that may be different from the one included here, so make sure your code does not depend on any specific layer 1 implementation details!
Layer 1 -- reading/writing a single byte:
Layer 1 maintains byte ordering, such that the receiver receives the first byte sent before receiving the second byte sent, etc.
You can (and should) assume that any layer 1 implementation maintains the order of individual bytes.
The layer 1 interface protocol is specified here, but you do not need to write this layer, just use it. A sample implementation is provided that will support testing of your code (the sample layer will support half-duplex communication using a pipe).
The specific layer 1 API is shown below. Each function returns an int that indicates the number of bytes read/written; therefore, the return value should be 1 if everything goes well. A return value of -1 indicates that some kind of error occurred.
/* Writes the byte specified by b. Returns 1 on success or -1 on error. */ int l1_write( char b ); /* Reads one byte and copies the byte to the address specified by b. Returns 1 on success, -1 on error. */ int l1_read( char * b );
Layer 2 -- reading/writing a chunk:
Layer 2 provides the transmission and receipt of a chunk.
A chunk is defined as a sequence of bytes whose length is no greater than 16 bytes.
Each of the bytes in a chunk can have any value; the only restriction is that there are no more than 16 bytes.
Note that it is valid to send/receive a chunk of size 0.
Note that the term "chunk" is just being used so we have a formal name for the payload of layer 2. There's no such (well-defined) use of the term "chunk" in the real world of network programming.
The important issue in layer 2 is that there must be some agreement between the sender and the receiver as to what constitutes a chunk (e.g. how long is the chunk, where is the end of the chunk, etc.?). You must define a peer-to-peer protocol and implement the protocol. Further, the only way for l2_write() to send anything is by using l1_write(), and the only way l2_read() can retreive a byte from the sender is by calling l1_read().
The return value of each layer 2 function is the length of the chunk sent or received (in bytes) upon success, or -1 to indicate failure. Note that this return value must reflect the number of bytes in the original chunk; it must not count any extra bytes your layer 2 peer-to-peer protocol requires.
The specific layer 2 API is shown below. The l2_write() function sends a chunk that consists of the sequence of bytes starting at the address specified by the first parameter (chunk) with length len. The l2_write() function returns len on success, -1 on error. Note that you must handle all errors that can be detected here, meaning you must check for valid values of len, the return value of l1_write(), etc.
The l2_read() function reads a chunk and stores the incoming bytes in the buffer starting at the address specified by the first parameter (chunk). No more than max bytes will be put into chunk, so max limits the size of the chunk read. If a chunk is received by l2_read() that would require more than max bytes, l2_read() returns -1 (error). Upon successful reception of a chunk, the size of the chunk (the number of bytes stored in chunk) is returned.
int l2_write( char * chunk, int len ); int l2_read( char * chunk, int max );
Make sure that your l2_read() does not allow the sender to overflow the buffer! And it's not enough to recognize when this has happened and return an error; you must not store anything beyond the maxth location of chunk.
Layer 3 -- reading/writing a message:
Layer 3 provides the capability to send and receive messages, where a message is defined as any sequence of bytes.
There is no length limitation at layer 3, so a sender can ask layer 3 to send any size message whatsoever.
Your layer 3 implementation must use layer 2 to send and receive chunks, perhaps many times to transmit/receive a complete message.
The main issue at layer 3 is the creation of a peer-to-peer protocol that uses chunks to communicate. The receiver must know when it has reached the end of a message (i.e. when the last chunk is received). Your l3_write() function can only call l2_write() to send data (not l1_write()), and your l3_read() function can only call l2_read.
The specific layer 3 API is shown below. The l3_write() function sends a message (specified by msg) of length len and returns the number of bytes (of the message) sent on success (should be len) or -1 on error.
The l3_read() function reads a message and stores it in memory starting at the address specified by msg. No more than max bytes will be put into memory, so max must limit the size of the message read. The return value is the size of the message received or -1 on error. If a message is received by l3_read() that would require more than max bytes, l3_read() must return -1 (indicating an error).
int l3_write( char * msg, int len ); int l3_read( char * msg, int max );
Layer 4 -- reading/writing a message with error detection:
Layer 4 adds simple error detection to the services provided by layer 3.
The function prototypes (which define the interface protocols) look the same as the layer 3 prototypes; the only difference is that the layer 4 read function should return a -1 (error) if it detects an error in the received message.
The errors to look for here involve transmission errors; we want to somehow make sure that the message received is the same as the message that was sent.
Error Detection: The simplest approach to error detection is to use a checksum. To use a checksum, simply add all of the bytes of the original message; the checksum is the sum modulo 256 (i.e. to fit it in a single byte). The resulting byte should be sent along with the message data; the receiving end goes through the same steps to compute the checksum, then compares the received checksum to the computed checksum. If they don't agree, there was a transmission error. If the checksums do agree, it is likely there was no error (but it's not guaranteed). Many network protocols use checksums to detect errors, though they generally use checksums larger than a single byte to improve the accuracy of the error detection. When testing your code, assume that the TAs will use a bug-ridden layer 1 that introduces errors, so make sure your error detection works!
The specific layer 4 API is shown below. The l4_write() function sends a message (specified by msg) of length len and returns the number of bytes (of the message) sent on success (should be len) or -1 on error.
The l4_read() function reads a message into memory starting at the address specified by msg. No more than max bytes will be put into memory, so max must limit the size of the message read. If a message is received by l4_read() that would require more than max bytes, l4_read() must return -1 (error). If your error-detection mechanism detects transmission error(s), l4_read() must return a -1. Upon successful receipt of a message, the size of the message (i.e. the number of bytes stored in msg) is returned.
int l4_write( char * msg, int len ); int l4_read( char * msg, int max );
Note that you do not need to consider the possibility of dropped (or duplicated) bits or bytes; only worry about some of the bits being scrambled during transmission.
Layer 5 -- reading/writing a struct:
Layer 5 provides higher layers with a mechanism for sending and receiving a simple C struct (defined below).
The sending function (i.e. l5_write()) is given the address of the struct to be sent, and the corresponding call to l5_read() in the peer will dynamically create an identical struct.
Both peer processes have the definition of the struct ahead of time (compiled in), and the functions are written with this knowledge.
Note that some languages (e.g. Java) have a mechanism to ensure the structures on each side match up (i.e. are the same "version" of code).
Your layer 5 implementation must use layer 4 to send and receive messages. Once again, you need to come up with a peer-to-peer protocol such that your layer 5 functions know what to expect and can work together successfully. It does not matter what your peer-to-peer protocol is, as long as your l5_write() function works successfully with your l5_read() function.
Below is the definition of the C struct that layer 5 must use:
typedef struct
{
char * firstname;
char * lastname;
int rin;
} student_rec;
Note that firstname and lastname are pointers to NULL-terminated C strings. Both of these strings can be up to 80 characters in length (note that this does not include the '\0' terminator character).
Also note that rin is a 4-byte integer, but you must not assume anything about byte ordering here! The receiver could use a different byte ordering than the sender (i.e. big endian vs. little endian), so you must write code that works no matter what byte ordering is used.
The specific layer 5 API is shown below. The l5_write() function sends the struct pointed to by stu to the receiver, returning 1 on success, -1 on error.
The l5_read() function must use l4_read() to somehow read a student_rec and put the received field values (firstname, lastname, and rin) into the student_rec pointed to by stu. You may assume that stu points to memory that has been allocated and is large enough to store the two pointers and the int. You must (in l5_read()) dynamically allocate memory of the appropriate size for the firstname and lastname fields. It is the responsibility of the caller to free this memory. The return value is 1 on success, or -1 on error.
int l5_write( student_rec * stu ); int l5_read( student_rec * stu );
Sample code and testing:
The hw1test.c program should give you an idea of how to test your code.
The program can be used to test your layer 5 code (which should use your layer 4 code, which in turn should use your layer 3 code, etc.).
Although the sample code only accesses the layer 5 functions directly, it is necessary that your layers are independent and can work with any other implementation of other layers. For example, we might use a different layer 2 implementation and everything should work correctly! Also keep in mind that when the TAs test your code, error conditions may be introduced.
The program calls layer 5 to send or receive a student record depending on whether it finds command-line arguments present. If there are 3 command-line arguments, the program assumes it is a sender and sends argv[1] as firstname, argv[2] as lastname, and argv[3] as the rin. If no command-line arguments are detected, the program tries to read a student record using l5_read(). If everything works, the reader program prints the record values to stdout.
Using the sample layer 1 code, you can run the program as a sender and pipe the output of the program to another copy of the program running as a receiver:
bash$ ./hw1 Kim Kardashian 98765 | ./hw1 Name: Kim Kardashian ID: 98765
Required deliverables:
You must provide one file for each of the four layers you write.
Layer 2 code must be in a file named l2.c, layer 3 code in l3.c, and so on.
Note that each of these files must have both the corresponding read and write functions.
To build the program, use gcc to build the program like this:
bash$ gcc -Wall -o hw1test hw1test.c l2.c l3.c l4.c l5.c
The executable program produced will be hw1test (if it actually compiles!), so you could then test the sender with something like this:
bash$ ./hw1test kim kardashian 98765
Due Friday 3/12 (extended to 3/12)
Our second assignment is to be completed individually. Do not share code or review anyone else's code. Work on this project is to be your own.
Submit your project via RPI LMS as a single compressed and zipped file (using tar and gzip); include only source code and documentation files.
To package up your submission, use tar and gzip. More specifically, create a compressed tar file using your RCS userid, as in goldsd.tar.gz, that contains your source files (e.g. main.c and file2.c); include a readme.txt file only if necessary. Here's an example:
bash$ tar cvf goldsd.tar main.c file2.c readme.txt main.c file2.c readme.txt bash$ gzip -9 goldsd.tar
Be sure to comment your code and include your name at the top of each file submitted.
CLARIFICATIONS AND HINTS:
(1) Be sure your proxy server handles HTTP responses that use the
Content-Length header --OR-- the Transfer-Encoding: chunked header.
(2) Test your proxy server using command-line telnet and Firefox!
Write an HTTP proxy server in C that supports filtering based on server domain name. Your proxy server must correctly handle GET, HEAD, and POST requests. And your server must refuse to process any request that specifies any other HTTP request method (or any other unknown request) sent by a client speaking HTTP version 1.0 or 1.1.
Your proxy must work with any HTTP client (browser); assume that we will test your server with a custom HTTP client written with the sole purpose to find problems with your proxy server.
Domain Filtering:
Your proxy server must be capable of filtering out requests to Web servers within some DNS domains.
More specifically, you must support both prefix and suffix filtering.
For example, your proxy could be instructed to filter out any request made to a server whose name ends in advertisements.com or whose name starts with www.cs.
When your server detects a request that should be filtered, your server should return an HTTP error 403 (forbidden), which means you need to send back an HTTP status line that indicates an error.
Your proxy server obtains, via the command line, the list of domains to be filtered. The first command-line argument will be the port number to use to receive connections (the listener); the remaining arguments (if any) are the domains (or domain suffixes) that should be filtered. Below is an example of a command line that could be used to run your proxy server on port 8888 and filter out requests to doubleclick.com and yimg.com:
./proxy 8888 doubleclick.com yimg.com
The filtering you must implement is based entirely on strings, which could include domain names and/or IP addresses. For example, if you are filtering 128.113, the following request line should result in a 403 error.
GET http://128.113.126.13/index.html HTTP/1.1
Key Requirements:
Server Output:
To track requests, your proxy server must print exactly one line (to stdout) for each request serviced.
The line should include the host name or IP address of the client, plus the original request-line sent by the client (not including any headers that accompanied the request).
Also print one line for each filtered request, indicating that your proxy server did not process the request.
For example, the following might be the output generated by your proxy server if it received requests from a client running on monica.cs.rpi.edu:
bash$ ./proxy 8888 doubleclick.com slashdot.org monica.cs.rpi.edu: GET http://www.cs.rpi.edu/ monica.cs.rpi.edu: GET http://www.cs.rpi.edu/gfx/backg5.jpg monica.cs.rpi.edu: GET http://www.cs.rpi.edu/gfx/logo.jpg monica.cs.rpi.edu: GET http://www.slashdot.org/foo/blah [FILTERED] monica.cs.rpi.edu: GET http://www.yahoo.com/images/annakornikova.jpg monica.cs.rpi.edu: GET http://fred.com/purchase.asp?prod=17&qty=102 monica.cs.rpi.edu: HEAD http://www.slashdot.org/ [FILTERED] monica.cs.rpi.edu: POST http://www.fbi.gov/insecuresubmission.cgi
Due Wednesday 4/11
Our third assignment is to be completed individually. Do not share code or review anyone else's code. Work on this project is to be your own.
Submit your project (server code only!) via RPI LMS as a single compressed and zipped file (using tar and gzip); include only source code and documentation files.
To package up your submission, use tar and gzip. More specifically, create a compressed tar file using your RCS userid, as in goldsd.tar.gz, that contains your source files (e.g. main.c and file2.c); include a readme.txt file only if necessary. Here's an example:
bash$ tar cvf goldsd.tar main.c file2.c readme.txt main.c file2.c readme.txt bash$ gzip -9 goldsd.tar
Be sure to comment your code and include your name at the top of each file submitted.
CLARIFICATIONS:
(1) If a message is split into multiple chunks, each chunk may be
sent as a separate packet/datagram. This allows the sender to
send (stream) data as it becomes available.
(2) The maximum chunk size is indeed 999 bytes. Anything larger
than 99 bytes requires chunking -- there will always be a
minimum of two chunks (given that the last chunk will be size zero).
(3) Omit (or comment out) the "random image" requirement. We will
address that in Project #4.
(4) The server can send chunks to client(s) as they are received.
In other words, your server does not have to wait for all chunks
to be received before forwarding to client(s). Though see (14)
below!
(5) Chunking only need be supported via TCP. :-) This eliminates
a lot of the problems introduced by the unreliability of UDP.
Therefore, UDP is only used for 1-packet/datagram messages.
(6) To test your server, use telnet (TCP) and nc (TCP and UDP).
UDP is supported in nc via the -u flag. Further, you can
redirect a text file into nc, as in:
bash$ nc localhost 8765 < file.txt
Also for testing, feel free to use and modify p3client.c
and/or p3clientV2.c.
(7) For chunked messages received by the server, the server need
only send these to connected TCP clients. More specifically,
there is no need to forward chunked messages to UDP clients
(since UDP clients will not support chunking).
(8) Though not secure, you may assume that a UDP client is uniquely
identified by its IP address and port number. This is not secure
since UDP clients might connect using different client-side port
numbers (e.g. multiple nc program executions).
(9) If the first message received from a new client is not "I AM ...."
then ignore it and/or close the connection.
(10) There is no need to support name changes (i.e. on a TCP connection,
multiple "I AM ...." messages); for such cases, return ERROR (and
keep the current user logged in).
(11) Avoid '\r' in your implementation. We will NOT use telnet to
test your chat server.
(12) You can assume that there is no data loss for TCP and UDP.
In other words, if a message indicates X bytes in the payload,
you can assume X bytes will be successfully transmitted and
received. Further, all chunks will be sent intact and in order.
(13) For chunking, the final chunk will be indicated by either "C0"
or "C0\n" -- this is your choice (i.e. with or without the '\n'
character). Also feel free to support both.
(14) While chunked messages are being transmitted, other messages
might be interleaved (and misinterpreted) by a receiving client.
Therefore, choose (a) or (c):
(a) Force all chunks to be sent/received together
(e.g. blocking or queueing up msgs from CLIENT C etc.)
(c) Accumulate chunks; once you have them all,
send them all at once.
See the 4/9 in-class notes for more details.
Using a programming language of your choice (C, C++, Java, Perl, Python), write a chat server that meets the requirements below. Note that this is not just a text-based chat server, and it must support both TCP and UDP connections from clients.
Your chat server must accept messages on a well-known port number. The server must run from the command-line with the port number as the first command-line argument. Here's the required command-line format:
bash$ chat_server <port>
All communication must be sent via your server. You may use any approach you like, though at a minimum, your server must support at least 32 concurrent clients (plus both TCP and UDP connections).
Authentication is not required. When a client connects, it logs in by sending the following message:
I AM <userid>
The userid must be unique and must not contain any space characters. Your chat server responds to an I AM request with a line that contains either OK or ERROR. An error condition you must handle is if a duplicate user attempts to connect. Other errors may exist.
Messages your server must support are SEND or BROADCAST. The SEND message must be followed by a space character, then the target userid (see extra credit below). A BROADCAST message is sent to all users. Both end in a newline. Note that there is no required response from the server.
If a message is 99 bytes or less, a two-digit decimal number specifies the length of the message. Otherwise, if a message is larger than 99 bytes, a chunking approach is used (similar to HTTP) to send the message (which could be text, an image file, any file, etc.). Unlike HTTP, you must use decimal (instead of hexadecimal) chunk sizes of up to 999 bytes. Chunk lengths will vary and are specified with a C prefix. Be sure to send a chunk of size 0 to indicate the end of the message. Here are two example chunks:
C104 abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz C0
When a message is sent to a recipient, the FROM header specifies who the message is from (see examples below).
After every four messages sent to a client, your server must sent a random message from one of the previous four senders (selected randomly). Make up at least ten random messages (including the ones listed below), plus at least three embarrassing images of Shirley Ann Jackson that are stored on the server:
Your server must not crash. Further, be sure to handle as many error conditions as you can detect. For example, if a client disconnects before all chunks are received/sent or before the message is received/sent, handle this quitely and gracefully in the server. Other errors to handle include messages sent to users that are not online or non-existent; in such cases, either drop the message or fill in with randomly generated message(s).
Your server must not output anything, unless a -v option (verbose) is specified on the command-line. In verbose mode, display all messages sent to and from the server. Use the following format (note that \n characters are shown where required in transmitted packets/datagrams):
RCVD from 128.213.56.11: I AM ladygaga\n SENT to 128.213.56.11: OK\n RCVD from 128.213.56.13: I AM selenagomez\n SENT to 128.213.56.13: OK\n RCVD from 66.195.8.137: I AM ladygaga\n SENT to 66.195.8.137: ERROR\n RCVD from ladygaga (128.213.56.11): SEND selenagomez\n 9\n You suck! SENT to selenagomez (128.213.56.13): FROM ladygaga\n 9\n You suck! RCVD from ladygaga (128.213.56.11): SEND selenagomez\n C108\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n C8\n got it?! C0 SENT to selenagomez (128.213.56.13): FROM ladygaga\n C108\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n step off my turf!\n C8\n got it?! C0 etc. SENT (randomly!) TO selenagomez (128.213.56.13): FROM ladygaga\n 7 No way! RCVD from selenagomez (128.213.56.13): BROADCAST\n 15\n Leave me alone! SENT to selenagomez (128.213.56.13): FROM selenagomez\n 15\n Leave me alone! SENT to ladygaga (128.213.56.11): FROM selenagomez\n 15\n Leave me alone! etc.
Extra Credit: Extend the SEND request such that multiple userids can be included (space-delimited). For example:
SEND selenagomez parishilton ladygaga etc.
Due Sunday 5/13
Our fourth (and final) assignment is to be completed individually. Do not share code or review anyone else's code. Work on this project is to be your own.
Submit your project (client-side only!) via RPI LMS as a single compressed and zipped file (using tar and gzip); include only source code, image files, and documentation files, as applicable.
To package up your submission, use tar and gzip. More specifically, create a compressed tar file using your RCS userid, as in goldsd.tar.gz, that contains your source files (e.g. chat.html and chat.css); include a readme.txt file only if necessary. Here's an example:
bash$ tar cvf goldsd.tar chat.html chat.css readme.txt chat.html chat.css readme.txt bash$ gzip -9 goldsd.tar
Be sure to provide comments within your source files; include your name at the top of each file submitted.
CLARIFICATIONS: (1) For this project, this file describes the protocol that your chat client must implement. Note that there are some additions beyond that of Project #3 (in part to differentiate between broadcast and private messages the client receives). (2) For your chat client to communicate with the server, use HTML5 WebSockets. To test your client, you may use your Project #3 submission (though you'll need to modify it to act as a WebSockets server). (3) Cycle through at least 16 colors for the usernames, then repeat colors as necessary. (4) You can use jQuery if you'd like to. (5) You no longer need to provide validation links, since you will not have a URL to send to the validation page. Nonetheless, be sure your HTML5 and CSS2 (or CSS3) all validates! (6) Uploading files is no longer required; instead, you may consider this extra credit. (7) Okay, NO CHUNKING IS REQUIRED FOR THIS PROJECT! :-)
Chat Client: Based on the chat server from Project #3 above, write a chat client using HTML5, HTML5 WebSockets, CSS, and JavaScript. Use TCP/WebSockets to connect to the chat server (one will be provided to you). Match the screen shot below exactly, which includes color-coding your usernames, placing your RCS userid in the upper-right hand corner, etc. (see extra credit below).
Your chat client must connect to the chat server on port 8787 (you may hardcode the server hostname and port number). When you submit your project, be sure the hostname is set to localhost and the port number is 8787.
Your chat client must validate; at all times, provide links to the online validator; more specifically, provide a link to the HTML validator beneath every page of your client, plus a link to the CSS validator. If your code does not validate, you will lose points. Mimic the links and images at the bottom of this project page.
When your client starts up, users must specify their username, which (once validated by the client) is sent to the server via an I AM command. If an error occurs here (or elsewhere), use JavaScript alert() messages.
Messages sent via the "main" page (shown above) are sent to the chat server via the BROADCAST command.
A new USERS command must be sent by your client to obtain a list of all users currently logged in to the server. The USERS message ends in a newline character; once received, the chat server responds with a comma-separated list of all usernames, followed by a newline, as shown below. Note that you can assume no spaces will be in this response; further, all usernames will be valid (i.e. lowercase letters only, up to 15 characters in length, no duplicates).
ladygaga,selenagomez,parishilton,justinbieber,etc.
When a user clicks on a username anywhere within the "main" interface, the user interface opens a new window that allows the user to send and receive private messages (as shown below). Likewise, when a private message is received, show it in a new window. If a window is already open for a given username, send and receive all messages from that window (i.e. do not blindly open a new window for each private message exchange).
No action by the user should cause the page to reload (i.e. avoid using "old-fashioned" forms that post request data to a server-side script).
Your client must be able to accept asynchronous communication from the chat server. More specifically, the chat server may send any number of messages to the client at any time. Use HTML5 WebSockets, which is supported in beta form by Firefox and Google Chrome. You may assume that all messages are correct according to the protocol and rules specified in Project #3. Also see this summary of the Project #4 protocol specifications.
To test your client, network with your friends in class. Each of you can use a different client as long as one of you is running the chat server. You might also test by using multiple tabs/windows or even multiple browsers.
Extra Credit: Users must also be able to send image (or other) files to each other via private messages or broadcast. You may assume that all files sent are image files and will be uploaded by the client as a stream of bytes. Add the necessary browse and send buttons. When your client receives an image, display the image in a new window that frames the image and requires no scrollbars whatsoever.
Extra Credit: Add support for inline images, meaning a client can send image data as part of their text (e.g. for smileys).
Extra Credit: In addition to the required layout shown above, create a chat client of your own design. Be sure that your chat client layout meets the same set of functionality requirements described above.
Important Note: Other "bells and whistles" is nice, but please be sure your solution meets all the required specifications! Do not make changes to the protocol, required layout, etc., lest you wish to lose points! For this project, we may stifle some of your creativity, but only to ensure all of the required functionality works and is straightforward for our TAs to test and grade.