Skip to content

Reading P3 Packets

This section covers the steps to read a P3 packet. Since P3 is the same whether carried over dialup modem or TCP/IP, the steps are accordingly the same (though how the steps are implemented may differ due to underlying differences in lower layers, APIs, etc.)

Client Differences

While P3 is mostly the same across different clients, there are some observed differences:

  • The presence or absence of the length field.
  • Whether the crc field is required to have a valid value.
  • How the crc field is encoded.

For example, the Quantum Link client does not have a length field, requires a valid crc, and encodes the crc in four bytes. The 3.0 America Online client does have a length field, requires a valid crc, and encodes the crc across two bytes. The 4.0 and later America Online clients do have a length field, require a valid crc only for the initial handshaking, and encode the crc across two bytes.

If Length is Not Present

The less complicated scenario is when the dialect of P3 being used does not have a length field. In this case, read and validate the sync field. Then read the crc field, and keep reading until you get to a byte with the value 0x0D. After that, validate the crc field.

flowchart
    step1(Read the sync field)
    validate1(Validate the sync field)
    discardPacket(Discard packet)
    step2(Read the crc field)
    step3(Read through the byte 0x0D)
    validate2(Validate the CRC)
    nakCRC(Send NAK: invalid crc)
    processPacket(Process packet)

    step1 --> validate1
    validate1 -- invalid --> discardPacket
    validate1 -- valid --> step2
    step2 --> step3
    step3 --> validate2
    validate2 -- invalid --> nakCRC
    validate2 -- valid --> processPacket

If Length is Present

If the dialect of P3 being read does have a length field, there are more steps to be peformed as compared to the scenario where it does not exist.

In effect, first read and validate the sync field. Then read the crc and length fields. Then read the number of bytes as prescribed in the length field. Finally, read and validate the msg_end field.

flowchart TD
    step1(Read the sync field)
    verify1(Verify the sync field)
    discardPacket(Discard packet)
    step2(Read the crc field)
    step3(Read the length field)
    step4(Read length amount of bytes)
    verify4(Verify the CRC)
    nakCRC(Send NAK: invalid crc)
    step5(Read the msg_end field)
    verify5(Verify the msg_end field)
    processPacket(Process packet)

    step1 --> verify1
    verify1 -- invalid --> discardPacket
    verify1 -- valid --> step2
    step2 --> step3
    step3 --> step4
    step4 --> verify4
    verify4 -- valid --> step5
    verify4 -- invalid --> nakCRC
    step5 --> verify5
    verify5 -- invalid --> discardPacket
    verify5 -- valid --> processPacket

Additional Notes

For purposes of efficiency, it may be possible to read the fixed bytes at the start of a P3 packet in a single request. For example, if the P3 dialect does include a length field, then a single read request for 5 bytes will usually suffice. You must however still validate the value of the sync field.

The following list of "lessons learned" have come from working with different P3 implementors and assisting them with troubleshooting faulty P3 implementations:

  1. Do not assume the first byte received from a lower network layer will always be 0x5A. Actually verify the sync field before assuming the start of a P3 packet.
  2. Do not assume a full P3 packet will be transmitted by the lower layer in a single read()/recv() request. Be prepared to handle partial P3 packets, buffering as necessary.
  3. Do not assume only one P3 packet will be transmitted by the lower layer in a single read()/recv() request. Be prepared to handle multiple P3 packets in a single request, parsing and buffering as necessary.
  4. If working with a P3 dialect that includes the length field, do not assume the first 0x0D byte encountered is the msg_end field. It is perfectly fine to have embedded 0x0D values in this case.