23.10 Peeling Off an Association
We have been focusing on the one-to-many-style
interface provided by SCTP. This interface has several advantages
over the more classic one-to-one style:
-
There is only one file descriptor to
maintain.
-
It allows us to write a simple iterative
server.
-
It lets an application send data on the third
and fourth packet of the four-way handshake by using
sendmsg or sctp_sendmsg to implicitly establish
the connection.
-
There is no need to track transport state. In
other words, the application just does a receive call on the socket
descriptor and does not need to do any of the traditional
connect or accept function calls before receiving
messages.
However, there is one major drawback to this
style. It makes it difficult to build a concurrent server (either
using threads or by forking children). This drawback has brought
about the addition of the sctp_peeloff function.
sctp_peeloff takes a one-to-many socket descriptor and an
association ID and returns a new socket descriptor with just that
association (plus any queued notifications and data on that
association) attached in a one-to-one style. The original socket
remains open, and any other associations represented by the
one-to-many socket are left unaffected.
This socket can then be handed off to either a
thread or a child process to execute a concurrent server. Figure 23.15 illustrates a further
modification to our server that processes the first message of a
client, extracts the client socket descriptor using
sctp_peeloff, forks a child, and calls our original TCP
str_echo function introduced in Section 5.3. We use
the address of the received message to call our function that gets
us the association ID (Section 23.8). The
association ID is also available in sri.sinfo_assoc_id; we
show this method of determining the association ID from the IP
address to illustrate another method. After forking the child, our
server loops back to process the next message.
Figure 23.15 A
concurrent SCTP server.
sctp/sctpserv_fork.c
23 for ( ; ; ) {
24 len = sizeof(struct sockaddr_in);
25 rd_sz = Sctp_recvmsg(sock_fd, readbuf, sizeof(readbuf),
26 (SA *) &cliaddr, &len, &sri, &msg_flags);
27 Sctp_sendmsg(sock_fd, readbuf, rd_sz,
28 (SA *) &cliaddr, len,
29 sri.sinfo_ppid,
30 sri.sinfo_flags, sri.sinfo_stream, 0, 0);
31 assoc = sctp_address_to_associd(sock_fd, (SA *) &cliaddr, len);
32 if ((int) assoc == 0) {
33 err_ret("Can't get association id");
34 continue;
35 }
36 connfd = sctp_peeloff(sock_fd, assoc);
37 if (connfd == -1) {
38 err_ret("sctp_peeloff fails");
39 continue;
40 }
41 if ( (childpid = fork()) == 0) {
42 Close(sock_fd);
43 str_echo(connfd);
44 exit(0);
45 } else {
46 Close(connfd);
47 }
48 }
Receive and process first message from
client
26鈥?0
The server receives and processes the first message a client
sends.
Translate address to association
ID
31鈥?5
The server next uses our function from Figure 23.13 to
translate the address to an association ID. If for some reason the
server cannot get an association ID, it skips this attempt to fork
a child and instead will try with the next message.
Extract association
36鈥?0
The server extracts the association into its own socket descriptor
with sctp_peeloff. This results in a one-to-one socket
that can be passed to our earlier TCP version of
str_echo.
Delegate work to child
41鈥?7
The server forks a child and lets the child perform all future work
on this new socket descriptor.
|