First, represent a as 10 and b as 01. By using this technique, you can identify any single-bit error, but not correct it - if you get 11, you know that's wrong, but you can't tell whether it's supposed to be a or b. (Choosing 01 and 10 rather than 00 and 11 protects against the kind of error where the channel is dead and always reports zeros, or ones). After reading this full stream, the recipient will have a result that contains three states: definitely a, definitely b, or unknown (we deliberately ignore the possibility of getting errors in two consecutive bits; we can catch that case later).
Then, after transmitting your entire stream, send a checksum of the correct data. Use a well-known checksumming algorithm like md5 that is known to have good properties, rather than rolling your own, which almost certainly won't work as well. Transmit the checksum using the same 10 / 01 encoding as the rest of the content.
Here's how the recipient of the data can figure out the corrected data from the original stream in conjunction with the checksum: Assume each "definitely known" bit is correct, but try both values for each unknown bit (in both the data and the checksum). Calculate the checksum of the data and see if it matches the checksum that was sent. This algorithm is O(2^n) in the number of "unknowns" found, but that number is expected to be low. If the number is too high to calculate in a reasonable amount of time, the transmission can be flagged as corrupt and the user can try again. If all has gone well, though, there should be exactly one combination of values for the unknown bits that leads to the checksum matching. Voila, you've now got your correct sequence of 'a's and 'b's.
There are a couple of ways that this can fail to give a corrected stream, but even those cases can be identified. One is mentioned above - too many "unknowns". Another is if there were errors in two consecutive bits, turning a b into a "definite a". That situation will be flagged because no combination of values for the "unknowns" will result in a matching checksum. The other possibility is the astronomical possibility that an incorrect sequence of 'a's and 'b's would generate a correct checksum. With a good checksumming algorithm, that should be absolutely impossible without also producing a too-large number of errors (and hence "unknowns"), so the transfer would be flagged as corrupt anyway.
