Partial delivery will be used by the SCTP implementation any time a "large" message is being received, where "large" means the SCTP stack deems that it does not have the resources to dedicate to the message. The following considerations will be made by the receiving SCTP implementation before starting this API:
The amount of buffer space being consumed by the message must meet or exceed some threshold.
The stack can only deliver from the beginning of the message sequentially up to the first missing piece.
Once invoked, no other messages may be made available for the user until the current message has been completely received and passed to the user. This means that the large message blocks all other messages that would normally be deliverable, including those in other streams.
The KAME implementation of SCTP uses a threshold of one-half the socket receive buffer. At this writing, the default receive buffer for the stack is 131,072 bytes. So, without changing the SO_RCVBUF, a single message must be larger than 65,536 bytes before the partial delivery API will be invoked. To further extend the new version of the server from Section 10.2, we write a utility function that wraps the sctp_recvmsg function call. We then create a modified server to use our new function. Figure 23.2 shows our wrapper function to handle the partial delivery API.
1215 If the function's static buffer has not been allocated, allocate it and set up the state associated with it.
1618 Read in the first message using the sctp_recvmsg function.
1922 If sctp_recvmsg returns an error or an EOF, we pass it directly back to the caller.
2324 While the message flags show that the function has not received a complete message, collect more data. The function starts by calculating how much is left in the static buffer.
2534 Whenever the function no longer has a minimum amount of room left in its receive buffer, it must grow the buffer. We do this using the realloc function to allocate a new buffer of the current size, plus an increment amount, and copy the old data. If for some reason the function cannot grow its buffer any more, it exits with an error.
3536 Gather more data with the sctp_recvmsg function.
3738 The function increments the buffer index and goes back to test if it has read all of the message.
3940 When the loop terminates, the function copies the number of bytes read into the pointer provided by the caller and returns a pointer to the allocated buffer.
sctp/sctp_pdapircv.c
1 #include "unp.h" 2 static uint8_t *sctp_pdapi_readbuf = NULL; 3 static int sctp_pdapi_rdbuf_sz = 0; 4 uint8_t * 5 pdapi_recvmsg(int sock_fd, 6 int *rdlen, 7 SA *from, 8 int *from_len, struct sctp_sndrcvinfo *sri, int *msg_flags) 9 { 10 int rdsz, left, at_in_buf; 11 int frmlen = 0; 12 if (sctp_pdapi_readbuf == NULL) { 13 sctp_pdapi_readbuf = (uint8_t *) Malloc(SCTP_PDAPI_INCR_SZ); 14 sctp_pdapi_rdbuf_sz = SCTP_PDAPI_INCR_SZ; 15 } 16 at_in_buf = 17 Sctp_recvmsg(sock_fd, sctp_pdapi_readbuf, sctp_pdapi_rdbuf_sz, from, 18 from_len, sri, msg_flags); 19 if (at_in_buf < 1) { 20 *rdlen = at_in_buf; 21 return (NULL); 22 } 23 while ((*msg_flags & MSG_EOR) == 0) { 24 left = sctp_pdapi_rdbuf_sz - at_in_buf; 25 if (left < SCTP_PDAPI_NEED_MORE_THRESHOLD) { 26 sctp_pdapi_readbuf = 27 realloc(sctp_pdapi_readbuf, 28 sctp_pdapi_rdbuf_sz + SCTP_PDAPI_INCR_SZ); 29 if (sctp_pdapi_readbuf == NULL) { 30 err_quit("sctp_pdapi ran out of memory"); 31 } 32 sctp_pdapi_rdbuf_sz += SCTP_PDAPI_INCR_SZ; 33 left = sctp_pdapi_rdbuf_sz - at_in_buf; 34 } 35 rdsz = Sctp_recvmsg(sock_fd, &sctp_pdapi_readbuf[at_in_buf], 36 left, NULL, &frmlen, NULL, msg_flags); 37 at_in_buf += rdsz; 38 } 39 *rdlen = at_in_buf; 40 return (sctp_pdapi_readbuf); 41 }
We next modify our server in Figure 23.3 so that it uses the new function.
2930 Here the server calls the new partial delivery utility function. The server calls this after nulling out any old data that may have been hanging around in the sri variable.
sctp/sctpserv05.c
26 for ( ; ; ) { 27 len = sizeof(struct sockaddr_in); 28 bzero(&sri, sizeof(sri)); 29 readbuf = pdapi_recvmsg(sock_fd, &rd_sz, 30 (SA *) &cliaddr, &len, &sri, &msg_flags); 31 if (readbuf == NULL) 32 continue;
3132 Note that now the server must test for NULL to see if the read was successful. If not, the server just continues.