BASIC BULLETIN BOARD AND CHAT SERVER Programmers: Ritesh Jain Ekta Manaktala Part 1: Implementation Log The server performs an initialization of the file list and client list. A linked list of the following structure is maintained: file name lock next pointer The linked list data structure itself contains a file list lock, a pointer to the head of the linked list. The client list contains a client list lock and a pointer to the head of a linked list with the following structure: client login client sock fd lock next pointer The file list is initialized to the list of files in the bboard directory using the opendir and readdir commands. The client list is initialized to NULL. The server accepts connections from different clients, adds the client to the client list and spawns a new thread for each connection it accepts. It also sets up another connection to the client in order to read port numbers during get and put operations as explained later. In the per-client thread, the server reads command identifiers from the client. The following command ids are used: Command Id ------------------ LOGOUT (0) GET (1) PUT (2) WHO (3) LS (4) DELETE (5) DEFAULT (6) /* chat */ Note: 1. The server does not lock the client before a read. This is because only the per-client thread can read from the client sock fd. So no concurrency testing is necessary. 2. A global total order is maintained on the locks so that no deadlock occurs when multiple clients are running. The global order maintained is: Client list lock Per-client lock File list lock File lock 3. A special socket called "portSockFd" is set up to send port numbers from the client to the server for file transfer operations. This port number is used by the server to create a TCP connection with the client over which file operations occur. Each get and put command sets up a separate connection, performs the file transfer and then tears down the connection. 4. Whenever get and put commands are executed, the file name and length are transferred across connections. In order to actually transfer the file contents, a new thread is spawned at the server so that the file transfer can take place concurrently with ls, get, chat and other commands. Also, a child process is created using fork at the client side to handle file transfers. Logout command: The server removes the client from the client list and closes all associated sockets. Get command: The client sets up a socket and binds it to the first unused port (by setting the sin_port, of the address to which it binds, to 0). It does a getsockname() on the socket to find out which port it is bound to. It sends this port number on "portSockFd" to the server. The server then does a connect() to this port number. Once the connection is established, the file name is transferred over this connection. Then the server sends the file length. Both the filename and the length are framed using length-based framing (using bboard_read/write from Prof. Lumetta's code). The server now sends the file in packets of 1024 bytes. No framing is used for the data packets. Put command: The client sets up a socket and binds it to the first unused port (by setting the sin_port, of the address to which it binds, to 0). It does a getsockname() on the socket to find out which port it is bound to. It sends this port number on "portSockFd" to the server. The server then does a connect() to this port number. Once the connection is established, the file name is transferred over this connection. Then the client sends the file length. Both the filename and the length are framed using length-based framing (using bboard_read/write from Prof. Lumetta's code). The client now sends the file in packets of 1024 bytes. No framing is used for the data packets. Who command: The server first locks the client list, then it locks the client which made the request, sends the client login names and then unlocks both the client and the client list. Ls command: The server locks the client, then it locks the file list, sends out the file names and then unlocks both the file list and the client. Delete command: The server reads the file name to be deleted, then removes the file by first locking the file list, searching for the file entry in the file list, locking the file, removing the entry from the file list, physically removing the file from the directory and then finally unlocks the file list. Default command: The server reads the chat message from the client, obtains a lock on the client list. It then traverses the client list, locking each client and sending the chat message and unlocking it. Finally it unlocks the client list. Files: server.h: Contains the definitions of structs used, namely clientRec (record of client data within the linked list), clientListRec (record containing head of the client linked list, client list lock, etc.), fileRec (record of file data within the linked list), fileListRec (record containing head of the file linked list, file list lock, etc.), threadInfoRec (record containing information to be passed to the per-client thread), fileThreadInfoRec (record containing information to be passed to the thread for file transfer). network.h: Contains include files and constants (like the common port numbers, buffer sizes, command identifiers, etc.) network.c: Contains helper functions like bboard_read, bboard_write, wrapperRead, and wrapperWrite. server.c: Contains server code. client.c: Contains client code. Part 2: Concurrency Analysis The algorithm for "@ls" at the server side is: - Lock the client which made the request - Lock the file list - Send the list of file names - Unlock the file list - Unlock the client The above algorithm blocks if it cannot obtain a lock immediately. So if another per-client thread has a lock on the current client or the file list, it blocks till the lock is released. Consider the following scenario: There are 3 clients. The first client is executing chat. Per-client thread 1 currently has a lock on file "foo" since it is executing a "@get foo" command. Per-client thread 2 has a lock on the file list and is waiting on the lock for file "foo" since it is executing a "@put foo" command. Now client 3 performs an "@ls" command and is waiting on the file list lock. So it waits till client 1 completes its file transfer operation and releases the lock on file "foo", client 2 obtains this lock, performs a file transfer operation and releases the file list lock. Now per-client thread 3 can obtain the file list lock and performs the @ls operation.