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:
- Do not assume the first byte received from a lower network layer will
always be
0x5A
. Actually verify thesync
field before assuming the start of a P3 packet. - 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. - 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. - If working with a P3 dialect that includes the
length
field, do not assume the first0x0D
byte encountered is themsg_end
field. It is perfectly fine to have embedded0x0D
values in this case.