TCP REPL commands

This section lists REPL commands for working with sockets. These commands should only be implemented on hosts.

There are two types of commands:

  • Basic commands are small wrappers around Socket API functions so that we can test them. These commands don’t do much more than call their respective API function (but see the sections on each command for details):
    • a: Listen and accept on a port (like a “server”)
    • c: Connect to a socket (like a “client”)
    • s: Send data using a socket
    • r: Receive data on a socket
    • cl: Close a socket
  • Other commands: are a bit more involved:
    • ls: List all sockets
    • sf: Use your Socket API to send a file
    • rf: Use your Socket API to receive a file

The following sections describe the format of each REPL command.

Note: This specification is not as strict as IP. In particular, we do not specify exact messages that should be printed when certain commands return, especially when errors occur. In cases where the specification does not give a specific format, you are free to choose your own behavior.

For example, if a command results in an error, you can choose what the error message looks like.

We will be grading your work manually, so any implementation that that can perform the actions specified here and produce the same output is sufficient. Therefore, please try to stick to the parts that are specified, but don’t worry if you need to take liberties on things that are not.

About socket IDs

In the REPL, all sockets are identified by an ID number so we can refer to them in commands. This means that you will need some way to map socket IDs to socket objects in your TCP stack.

Depending on how you implement your socket API, this mapping (ie, IDs to socket objects) may occur inside your TCP stack, or as part of your REPL.

c: Connect to a port

Connect to an IP and port using VConnect, returning an ID number for the socket.

Example

> c 10.0.0.1 9999

Specification

Format:

c <vip> <port>
Param Example Description
vip 10.0.0.1 Virtual IP of destination
port 8000 Destination port for TCP connection

On successful connection, no output is expected from this command. On error, a message should be printed indicating the error from VConnect.

a: Listen + Accept incoming connections

Create a listening socket on the specified port and create a thread to accept new connections asynchronously.

Format:

a <port>

where <port> is the TCP port on which to listen.

If the listen socket is created successfully, no output is expected from this command. If the socket creation fails, a message should be printed indicating the error.

What this command does

After creating the listen socket, this command should start up a thread/goroutine that continually calls VAccept to accept new connections from clients, like this (in pesudo-Go):

func ACommand(port) {
    listenConn := VListen(port)
	
	for {
		clientConn, err := listenConn.VAccept()
		// . . .
	}
}

The idea here is to emulate the behavior of a server accepting connections (like your Snowcast server). Once a client connects, this thread doesn’t need to do anything with the new client socket, so long as it is usable by other REPL commands (eg. s, r, etc.) afterward.

After the client socket has been created, the thread should continue accepting further connections.

s: Send to a socket

Example

Send bytes to a socket using VWrite.

> s 0 aaaa
Sent 4 bytes

Specification

Format:

s <socket ID> <bytes>
Param Example Description
socket ID 0 Socket ID number. MUST be a normal socket, not a listen socket
bytes hello ASCII string of data to send. Will not contain spaces (ie, you can stop parsing at any whitespace)

Note: Even though the REPL s command only sends strings, the VWrite function in your socket API MUST be able to send arbitrary bytes, since you will need to send files with arbitrary data (with sf and rf).

Since this command is just for testing your sockets, we just use it with simple strings to keep the text parsing easier. :smile:

This command MUST block until VWrite returns.

On a successful send, s should print the number of bytes sent to standard out. On error, a message should be printed indicating the error.

r: Receive

Read data on a socket using VRead. This command MUST block until VRead returns.

Example

> r 0 5
Read 5 bytes:  hello

Format

> r <socket ID> <numbytes>

Param Example Description
socket ID 0 Socket ID to read from
numbytes 10 Number of bytes to read

When VRead returns, this command should print the number of bytes returned, and any data.

Note: VRead may return fewer bytes than the number of bytes requested with numbytes–this is expected. Your r command should print whatever number of bytes was returned by VRead. For example, the following behavior is valid:

> r 0 10
Read 2 bytes:  ab

ls: List sockets

List all sockets, along with their TCP state.

Example:

Here’s an example table with two sockets. Socket 0 is a listen socket, socket 1 is a socket for a client that connected to this listen socket.

> ls
SID      LAddr LPort      RAddr RPort       Status
  0    0.0.0.0  9999    0.0.0.0     0       LISTEN
  1   10.1.0.2  9999   10.0.0.1 46810  ESTABLISHED

Specification

Format (after first line):

<socket ID> <laddr> <lport> <raddr> <rport> <status>
Param Example Description
socket ID 0 Socket ID
laddr 10.1.0.2 Local IP address: this should be the IP address of this node, which is used as the source address on packets sent using this socket.
lport 9999 Local TCP port
raddr 10.0.0.1 Remote IP address: IP address of the other endpoint for this socket, which is used as the destination address on packets sent using this socket
rport 46810 Remote TCP port
status ESTABLISHED Connection state, per the TCP state machine

Notes

  • Listen sockets are not connected to a remote endpoint and thus have no remote IP/port. By convention, we write the remote IP and port for listen sockets as 0.0.0.0 and 0, respectively. Similarly, the local IP should also be 0.0.0.0.

cl: Close socket

Initiate connection teardown by calling VClose. This function MUST NOT block.

Format:

cl <socket ID>

Under normal operation, no output is expected after running this command. On error, a message should be printed describing the error.

sf: Send file

Send a file to an IP and port using your Socket API (VConnect, VWrite, VClose, etc.).

Example

> sf path/to/some_file 10.1.0.2 9999

Sent 1048576 total bytes

This command sends the file at path/to/some_file to address 10.1.0.2 on port 9999.

Specification

Format:

sf <file path> <addr> <port>

where <file path> is the path to a file and <addr> and <port> are the destination IP address and port numbers, respectively.

On successful operation, this command should print the total number of bytes sent–there is no required format to emulate, so long as you include this information.

On error, this command should print a message indicating the failure.

Notes

  • This command MUST operate asynchronously to the REPL. In other words, it must create a separate thread/goroutine that does the send process, so that you can run other REPL commands while the file transmission is in progress.
  • This command MUST NOT print all of the file’s contents to the terminal by default, as the file may be very large. (It’s okay to do this with your own logging/debugging config, though)

rf: Receive file

> rf path/to/some_destination_file 9999

[ . . .]
Received 1048576 total bytes

This command starts a new thread that waits for a connection on the specified listening port. After a client connects, it receives data until the sender closes the connection, writing the output to the destination file.

Specification

Format:

rf <dest file> <port>

where <destfile> is the path for the file to be written and <addr> and <port> is the port number to listen for a connection.

On successful operation, this command should print the total number of bytes received–there is no required format to emulate, so long as you include this information.

On error, this command should print a message indicating the failure.

Notes

  • This command MUST operate asynchronously to the REPL–in other words, it must create a separate thread/goroutine that does the send process, so that you can run other REPL commands while the file transmission is in progress.
  • This command MUST NOT print all of the file’s contents to the terminal by default, as the file may be very large. (It’s okay to do this with your own logging/debugging config, though)
  • If the destination file <dest file> exists, it MUST be overwritten (truncated) with the new contents. The resulting file should contain the bytes received on during the latest connection only
  • The listen socket opened by rf only needs to accept one client connection. You do not need to continuously check for new connections after the first client has connected. After the receive file operation finishes, the listen port should be closed.

Modifications for Congestion Control (capstone only)

Implementations with congestion control should add two additional REPL commands (lc, sc), as well as modify s and sf to support specifying a congestion control method.

Here’s a summary of the changes (again, we will not be strict on the formatting, so long as you can do these things):

Command Description
lc Prints the available congestion control algorithm names, eg. reno, tahoe, …
sc <socket ID> <string> Sets the congestion control algorithm for the given socket. To disable congestion control, use the string: none
s You should modify your ls command to also list the congestion control algorithm (if any) each socket is using, and the congestion window size as well.
sf You should modify your sf command to optionally take in a congestion control algorithm, with the options being: reno, tahoe, …. The default for no argument is none.

Reference only

The following commands are provided only in the reference version for debugging purposes. You do not need to implement them.

drop

Configures a probabilistic drop rate when forwarding packets. The input must be a number between 0 and 1. drop 0 forwards all packets, drop 1 drops all packets.

This command only works on the reference vrouter. You do not need to implement this.

Example

> drop 0.02

This example drops 2% of packets forwarded. No output is expected from this command.

Notes

  • Instead of specifying this command each time you start up, you can pass it as a command-line option when starting the router using --drop. For info, see here.
  • If you want to drop packets in a predictable way for testing, you can configure the random seed used when dropping packets using --drop-rate-seed. For more info, see here.