Bobscorn/ptop

Cyclic Redundancy Checks

Closed this issue ยท 8 comments

Cyclic Redundancy Checks

Proposed work:

  • Add CRC to each chunk
  • Make sender send only when there is space instead of blocking
  • Have acknowledgement state for each chunk on the sender side (a timer and an acknowledgement bool)
  • Sender resends chunks that time out or have received a 'bad' signal
  • Sender expires based on any file message not just file end
  • Sender/Receiver now count chunks they have sent/received and close/write when they have counted all chunks

Hit a bit of a snag.

Can't simply have the file receiver respond with 'bad chunk: x' as it's entirely possible the peer won't receive that message.
Same with acknowledgements, if the file receiver successfully receives a chunk and responds with an acknowledgement, it can't be sure the peer will receive that acknowledgement. Worst case scenario the peer that hasn't received the acknowledgement keeps sending the un-acknowledged chunk over and over, best case, the peer will timeout before it sends too much redundant traffic.

Initially I think 'we keep resending errors and acknowledgements until we get a response to those' but then we get stuck with the same situation with this new response that we wait for.

Currently trying to find how this kind of problem is solved elsewhere.

I think I'll just make the sender only resend a chunk 3 times, if it receiver doesn't get it.... then it's outside the scope of this project.

Perhaps using something like UDT or SCTP protocols would offer more reliable traffic.

Alright after a network-network test it seems it very unreliably works, most noticeable issue is that I received only 128 chunks at a time.

Increasing the number of times each chunk can be sent seems to allow the next 128 chunks through.

128 * 508 (our chunk size) = 65,532 which is 4 less than 2^16 or 64K, which is very suspicious.

After testing it seems like the OS internal buffer for our sockets is not being cleared quick enough.

As such here is a proposed rewrite of PtopSocket to use a dedicated thread for reading incoming data on a socket.

PtopSocket creates it's own thread in it's constructor, and it maintains a shared std::vector<std::vector> of messages across threads.
The extra thread, the polling thread, will now be solely responsible for taking data from the socket, and putting it into the shared messages object.
PtopSocket will instead of taking data directly from the socket, now take data from the shared messages object.

Other methods such as select_for with select_for::read and has_data will now check the shared messages object instead of the socket.

PtopSocket:

  • Creates the thread in the constructor
  • Maintains a thread-safe messages object
  • has_data(), receive_bytes() and select_for(select_for::read) check the shared messages object instead

Alright using a threaded data collector seems to have significantly increased the bandwidth, and reduced packets being dropped due to a full buffer.

However with the new found speed the file transfer process ends up severely congesting the network with large files, sending them at speeds well over network tolerable speeds (in excess of 100Mbps for files larger than a few MBs).

To compensate for this, I believe a 'bandwidth negotiation' stage before sending a file, designed solely to test the bandwidth and packet loss, before the sending of a file.
Something along the lines of sending 10MB of redundant data from sender to receiver, waiting and then informing the receiver how much data, and how many packets were sent, and then having the receiver respond with the number of packets they actually received.
This could happen multiple times, each time the sender adjusting it's speed based on the percentage of packets actually received.

This should enable the sender to send data at an efficient speed that doesn't congest the network.

This is more of a socket feature than a file-specific feature, but I believe it should be in this branch.

  • Implement message types for testing bandwidth/packet loss
  • Implement a negotiation phase for sockets to send test packets, and receive test packets
  • Ensure this negotiation phase isn't affected by other traffic forms (perhaps lock the socket while negotiating?)
  • Ensure this negotiation phase is called before any file transfer.
  • Ensure both sides of data transfer are aware of the same negotiated speeds.

completed in 2c33d8a