1. How does the remote client determine when a command's output is fully received from the server, and what techniques can be used to handle partial reads or ensure complete message transmission?

_answer here_

> Loop Until EOF Character is Received

>   - The client continuously reads from the socket using `recv()`, checking if the last byte received is `0x04` (EOF).
>   - If the EOF character is detected, the client knows the full response has been received.

> Buffering and Aggregation
> Since `recv()` may return partial data, the client should append data to a buffer until the EOF character is found.




2. This week's lecture on TCP explains that it is a reliable stream protocol rather than a message-oriented one. Since TCP does not preserve message boundaries, how should a networked shell protocol define and detect the beginning and end of a command sent over a TCP connection? What challenges arise if this is not handled correctly?

_answer here_

> Use a Terminator Character
> - The protocol can use a special delimiter, such as `\0`(null byte) for commands and `0x04` (EOF) for responses.
> Include Message Length in the Header
> - A fixed-size header can precede each message, specifying the number of bytes in the payload.
> Use a Structured Format (e.g., JSON, XML)
> - While not ideal for simple shell protocols, structured formats like JSON allow parsing with defined message start and end markers.
> Chanllenges:
> - TCP might split a message across multiple `recv()` calls, requiring the client to reconstruct it.
> - TCP might merge multiple commands into one `recv()` call, requiring the client to parse multiple messages properly.
> - If no end delimiter is used, the client may read incomplete commands, leading to unexpected behavior.


3. Describe the general differences between stateful and stateless protocols.

_answer here_

> A stateful protocol maintains session state between client and server across multiple interactions, whereas a stateless protocol treats each request as independent with no retained memory of past interactions.

> Stateful Protocols:
>    The server remembers previous interactions. Requires more server-side resources (e.g., memory, session tracking).
>    Examples:
>    - TCP (tracks sequence numbers, retransmissions).
>    - SSH (maintains authentication state).
>    - HTTP with Sessions (stores login cookies).

> Stateless Protocols:
>   Each request is independent; no session state is stored. Easier to scale since no session tracking is needed.
>   Examples:
>   - UDP (each packet is independent).
>   - HTTP (by default, unless cookies or tokens are used).
>   - DNS (each lookup request is processed independently).

4. Our lecture this week stated that UDP is "unreliable". If that is the case, why would we ever use it?

_answer here_

> Although UDP is unreliable, which does not guarantee packet delivery, order, or integrity, it is still useful for applications where speed and low latency are more important than reliability.
> Since there is no connection setup or retransmission, UDP is much faster than TCP, which lower the latency. 
> UDP allows sending packets to multiple recipients simultaneously, which is crucial for Streaming video/audio, Online gaming (real-time updates), IoT devices (sensor updates)
> DNS uses UDP because queries are small and fast (retrying is faster than using TCP).

5. What interface/abstraction is provided by the operating system to enable applications to use network communications?

_answer here_

> The operating system provides socket APIs that allow applications to communicate over a network using protocols like TCP and UDP.

> Example: 
> Berkeley Sockets API (socket.h)

> Standard API used in C, C++, Python, etc. for network communication.
> 
> Functions:
> - `socket()` → Creates a socket.
> - `bind()` → Binds a socket to an IP/port.
> - `listen()` → Marks a socket for incoming connections.
> - `accept()` → Accepts a new client connection.
> - `connect()` → Connects to a remote server.
> - `send()`/`recv()` → Sends & receives data.