1 Your Task

  1. Download the supplied skeleton code at here.1 (last update 17 July 2025)

  2. In sendrecv.py complete an implementation of MySender and MyReceiver.

    In MySender’s send_message function, receive a message in bytes, then format a message to be sent as a sequence of bits, then call self.channel.send_bits() with those bytes.

    In MyReceiver’s handle_bit_from_network function, receive a bit from the network, then call self.got_message_function() when those bytes represent a full message passed to MySender’s send_message function.

    To demonstrate the interface, we supply an example implementation which is partially functional. It transmits messages by converting each byte into bits, and adding a nul byte to separate messages. This satisfies some but not all of the requirements below.

    Your implementation must:

    1. Be able to send messages of any length between 0 and 1023 bytes, inclusive

    2. Allow messages to contain any sequence of bytes (of an appropriate length).

      (This means that, for example, we should be able to take some of the bits your send_message function produces and send a new message containing them without it being corrupted.)

    3. If a message’s bytes are corrupted, by changing the values of bits randomly and/or adding and removing bits randomly, then:

      1. You should detect this and avoid calling got_message_function with data that does not represent a valid message at least 99.99% of the time. (You should be able to achieve this with a 2 or more-byte checksum.)

      2. Other than possibly the next few messages or couple kilobytes of messages, future uncorrupted messages should be received correctly (regardless of where the corruption took place).

    4. Not use too much space to send messages:

      • Including any headers/separators, the average size of a random message of N bytes at most 120 bytes or N * 1.2 bytes, whichever is larger. (It’s okay if some pathological messages take up more space.)
    5. Be your own code and use only functionality built-in to python or supplied as part of its standard library. This means that it is permissible to use the zlib module’s crc32 function and the struct module. But it is not permissible to use, for example, python-pppd

  3. Test your implementation using test.py:

    • You can use python3 test.py send ... to send sample messages:

       python3 test.py --input-format binary --output-format binary 00001111 11110000
      
       python3 test.py --input-format hex --output-format hex FF00FF00 809080908090
    • You can use python3 test.py recv ... to receive bits:

       python3 test.py --input-format binary --output-format binary 111100011110000111111
       python3 test.py --input-format hex --output-format hex FF00FFFFFFF
    • You can use python3 test.py suite ... to run a test suite:

       python3 test.py suite
       python3 test.py suite --verbose
    • You can specify to only run parts of test suites by passing a regular expression to –only-test and/or –only-subtest:

       python3 test.py suite --only-test=empty-clean
       python3 test.py suite --only-test=tiny.*
       python3 test.py suite --only-test=corrupt-first-recovery-tiny1 --only-subtest='delete bit #.*'
       python3 test.py suite --only-test=corrupt-first-recovery-tiny1 --only-subtest='delete bit #2'

    If you want to run additional tests, you might find it useful to modify the list of tests in TESTS.

    When grading, we may run additional tests and/or inspect your code by hand. Although the supplied tests do not intentionally omit functionality you must implement, they may get unlucky and fail to detect an implementation which is actually broken and they do not test everything for all possible message sizes. (Also, because some requirements deal with chances of corruption, it may be possible for them to spuriously fail on a correct implementation, though I think that is unlikely.)

  4. Briefly document in a comment or docstring near the top of your sendrecv.py the frame format you choose.

  5. Submit your sendrecv.py by using the submission site.

2 Hints

  1. You can reuse the supplied bit to byte and byte to bit conversions..

  2. The struct modules pack or unpack to convert integers to bytes and vice-versa.

  3. Be careful about choosing ambiguous message delimters.

    For example, let’s say you choose 0000 as a delimiter for messages and to escape 000 with 0001 and we try to send the message 111111100.

    In this case, we’ll send 111111100 0000. There was no reason to escape anything. When we try to remove the delimiter from this, we’ll find the first 0000, and get 1111111, which is not our original message.

    The problem here is that the escaping is not sufficient to handle the end-of-message edge case. We could fix this by escaping every 0 instead of every 000, but that’s pretty inefficient. A more space-efficient solution is to choose a delimiter pattern that has a distinctive transition from 0s to 1s that allows us to escape less frequently. The choice of 010 in lecture and 01111110 in the textbook (Systems Approach section 2.3) are examples of this.


  1. If you want to work on portal or similar, you might find it useful to right-click the link, select copy link or copy URL from the drop-down menu, then run the command wget THAT_URL, where THAT_URL is the result of pasting the link/URL.↩︎