February 25, 2005

Notes on Network Programming

The accept() call takes three arguments. The function signature is such: int accept(int sd, struct sockaddr * addr, socklen_t * addr_len); Under Linux, the third argument doesn't have to be initialized with the size of the second argument while in Solaris, something like len = sizeof addr; must precede the accept call. Don't know why, but probably has something to do with the implementation of the library in Solaris using the addr_len value to do some sort of check.

When implementing a retry/timeout scheme for a network client, it'll usually look something like:


for (try = 0; try < MAX_TRIES; try++) {
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connect(sd, (struct sockaddr*) &a, sizeof a) == 0) {
do_something();
break;
}
sleep(1);
}

I ran into a problem with this until I read the man page for connect a bit more carefully and found that I couldn't make successive connect calls using the same socket descriptor. This was a bit puzzling until I remembered the way tcp works and that:

+---------+ ---------\ active OPEN
| CLOSED | \ -----------
+---------+<---------\ \ create TCB
| ^ \ \ snd SYN
passive OPEN | | CLOSE \ \
------------ | | ---------- \ \
create TCB | | delete TCB \ \
V | \ \
+---------+ CLOSE | \
| LISTEN | ---------- | |
+---------+ delete TCB | |
rcv SYN | | SEND | |
----------- | | ------- | V
+---------+ snd SYN,ACK / \ snd SYN +---------+
| |<----------------- ------------------>| |
| SYN | rcv SYN | SYN |
| RCVD |<-----------------------------------------------| SENT |
| | snd ACK | |
| |------------------ -------------------| |
+---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+
| -------------- | | -----------
| x | | snd ACK
| V V
| CLOSE +---------+
| ------- | ESTAB |
| snd FIN +---------+
| CLOSE | | rcv FIN
V ------- | | -------
+---------+ snd FIN / \ snd ACK +---------+
| FIN |<----------------- ------------------>| CLOSE |
| WAIT-1 |------------------ | WAIT |
+---------+ rcv FIN \ +---------+
| rcv ACK of FIN ------- | CLOSE |
| -------------- snd ACK | ------- |
V x V snd FIN V
+---------+ +---------+ +---------+
|FINWAIT-2| | CLOSING | | LAST-ACK|
+---------+ +---------+ +---------+
| rcv ACK of FIN | rcv ACK of FIN |
| rcv FIN -------------- | Timeout=2MSL -------------- |
| ------- x V ------------ x V
\ snd ACK +---------+delete TCB +---------+
------------------------>|TIME WAIT|------------------>| CLOSED |
+---------+ +---------+

TCP Connection State Diagram
Figure 6.


(taken from ethz.ch)
I was in a syn-sent state, so further connect calls would not be able to use the same socket descriptor. So, when implementing a timeout, it's important to grab a new socket descriptor before trying a connect(). This realization after 30 minutes of frustration.

Posted by argyle at February 25, 2005 01:10 PM
Comments
Post a comment









Remember personal info?