| viric ( @ 2007-04-27 19:37:00 |
| Entry tags: | programado |
TCP koŝmaro
Hodiaŭ mi eksciis gravan aferon pri TCP. Imagu la programeron:
{
s = socket(PF_INET, SOCK_STREAM, 0);
connect(s, ...);
send(s, ...);
close(s);
}
Laŭ vi, ĝi funkcias por sendi datumojn per TCP, ĉu ne? Senprobleme la datumoj alvenos la alian flankon, ĉu ne? NE, kaze ke la alia flanko ion sendas.
Se la alia flanko nenion sendas, ĉio bonos. Ni sendos la datumojn, poste FIN, kaj ĉion bonan.
Sed se nia TCP-monto (tcp stack) ricevis datumojn por la koncerna konektingo (socket), sed la programo ilin ne legas (t.n. read()), je nia close() nia TCP-monto sendos ne FIN, sed RST.
Ĝuste, kiam la alia flanko ricevas nian RST, ĝi forĵetas ĉiujn datumojn de la TCP-monto, kiujn la riceva programo ankoraŭ ne legis.
Do jen, nia programo sendis blindfide datumojn, kaj ili probable ne atingos la aliflankan programon.
Ve, kiom malfacilis tion malkovri! Mi ne trovis en la RFC aŭ en la libroj de Stevens tiom klaran eksplikon. La plej simila ekspliko kiun ili tie diras (kiu donas konsekvence la ĵusan rakonton) estas: Por certiĝi ke la programo de la alia flanko ricevis niajn sendaĵojn, ni devas atendi per recv(), kaj ricevi nulon (0).. Certe, se ricevi nulon, tio signifas ke la alia flanko sendis FIN, ne RST. Kaj ĝi devas sendi FIN nur se la programo (ne nur la TCP-monto) ricevis ĉiujn datumojn. Tial nia simpla ne-ricevanta programo povas sendi RST, kaj forfiki la datumojn ankoraŭ ne legitajn de la aliflanka programo.
Cetere, neniel gravas ĉu antaŭ ol sendi ni ruligas:
shutdown(SHUT_RD);
La ĝenerala solvo estas ĉiam fari legojn kaj skribojn en select()-buklo. Eĉ se ni volas nur sendi.