Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223008110134178007189001184100131

Setting Socket Options

Knowing that the size of the sending and receiving buffers for the default socket are quite large, you as an application designer might decide that a smaller set of buffers might be more appropriate. This might be especially significant if you are expecting several instances of the program to run on your system.

Options are set on sockets using the setsockopt(2) function. Its function prototype is given as follows:

					
#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int s,
    int level,
    int optname,
    const void *optval,
    socklen_t optlen);

				

This function closely resembles the getsockopt(2) function discussed earlier. The arguments for setsockopt(2) are listed as follows:

  1. The socket s to effect an option change upon.

  2. The socket level of the option.

  3. The option optname to set.

  4. The pointer optval to the value to be used for the new option value.

  5. The option value length optlen, in bytes.

The only real difference between this function's arguments and the getsockopt(2) argument list is that the last argument is passed by value only. It is an input value only in this case.

Applying the setsockopt(2) Function

Listing 12.2 shows a short program that changes the send and receive buffer sizes for a socket. After setting these options, the program obtains the actual sizes of these buffers and reports them.

Example 12.2. setsndrcv.c—Setting SOL_SOCKET Options SO_SNDBUF and SO_RCVBUF
 <$nopage>
001 1:   /* setsndrcv.c:
002 2:    *
003 3:    * Set SO_SNDBUF & SO_RCVBUF Options:
004 4:    */
005 5:   #include <stdio.h>
006 6:   #include <unistd.h>
007 7:   #include <stdlib.h>
008 8:   #include <errno.h>
009 9:   #include <string.h>
010 10:  #include <sys/types.h>
011 11:  #include <sys/socket.h>
012 12:  #include <assert.h>
013 13:
014 14:  /*
015 15:   * This function reports the error and
016 16:   * exits back to the shell: <$nopage>
017 					 <$nopage>
018 17:   */
019 18:  static void
020 19:  bail(const char *on_what) {
021 20:      if ( errno != 0 ) {
022 21:          fputs(strerror(errno),stderr);
023 22:          fputs(": ",stderr);
024 23:      }
025 24:      fputs(on_what,stderr);
026 25:      fputc('\n',stderr);
027 26:      exit(1);
028 27:  }
029 28:
030 29:  int
031 30:  main(int argc,char **argv) {
032 31:      int z;
033 32:      int s = -1;                /* Socket */
034 33:      int sndbuf=0;   /* Send buffer size */
035 34:      int rcvbuf=0;/* Receive buffer size */
036 35:      socklen_t optlen;  /* Option length */
037 36: 
038 37:      /*
039 38:       * Create a TCP/IP socket to use:
040 39:       */
041 40:      s = socket(PF_INET,SOCK_STREAM,0);
042 41:      if ( s == -1 )
043 42:          bail("socket(2)");
044 43:
045 44:      /*
046 45:       * Set the SO_SNDBUF Size:
047 46:       */
048 47:      sndbuf = 5000;  /* Send buffer size */
049 48:      z = setsockopt(s,SOL_SOCKET,SO_SNDBUF,
050 49:          &sndbuf,sizeof sndbuf);
051 50:      if ( z )
052 51:          bail("setsockopt(s,SOL_SOCKET,"
053 52:              "SO_SNDBUF)");
054 53:
055 54:      /*
056 55:       * Set the SO_RCVBUF Size: <$nopage>
057 					 <$nopage>
058 					 <$nopage>
059 56:       */
060 57:      rcvbuf = 8192;  /* Send buffer size */
061 58:      z = setsockopt(s,SOL_SOCKET,SO_RCVBUF,
062 59:          &rcvbuf,sizeof rcvbuf);
063 60:      if ( z )
064 61:          bail("setsockopt(s,SOL_SOCKET,"
065 62:              "SO_RCVBUF)");
066 63:
067 64:      /*
068 65:       * As a check on the above…
069 66:       * Get socket option SO_SNDBUF:
070 67:       */
071 68:      optlen = sizeof sndbuf;
072 69:      z = getsockopt(s,SOL_SOCKET,SO_SNDBUF,
073 70:          &sndbuf,&optlen);
074 71:      if ( z )
075 72:          bail("getsockopt(s,SOL_SOCKET,"
076 73:              "SO_SNDBUF)");
077 74:
078 75:      assert(optlen == sizeof sndbuf);
079 76:
080 77:      /*
081 78:       * Get socket option SO_SNDBUF:
082 79:       */
083 80:      optlen = sizeof rcvbuf;
084 81:      z = getsockopt(s,SOL_SOCKET,SO_RCVBUF,
085 82:          &rcvbuf,&optlen);
086 83:      if ( z )
087 84:          bail("getsockopt(s,SOL_SOCKET,"
088 85:              "SO_RCVBUF)");
089 86:
090 87:      assert(optlen == sizeof rcvbuf);
091 88:
092 89:      /*
093 90:       * Report the buffer sizes: <$nopage>
094 					 <$nopage>
095 91:       */
096 92:      printf("Socket s : %d\n",s);
097 93:      printf(" Send buf: %d bytes\n",
098 94:          sndbuf);
099 95:      printf(" Recv buf: %d bytes\n",
100 96:          rcvbuf);
101 97:
102 98:      close(s);
103 99:      return 0;
104 100: }
105  <$nopage>

The program is similar to the previous one. However, after the initial socket is created, the following steps are used to set the buffer sizes:

  1. The value of variable sndbuf is set to the value of the buffer size desired. In this case, line 47 shows the value of 5000 being assigned.

  2. The setsockopt(2) function is called, setting the option named SO_SNDBUF at level SOL_SOCKET according to the value of sndbuf. Errors are checked in lines 50 to 52.

  3. The SO_RCVBUF option is set in the same manner as step 2, except that the buffer size is chosen as 8192 instead of 5000 bytes.

  4. Source code that follows at line 68 to the end of the program will fetch these option values from the kernel and report what has been established.

The following output shows a compile and execute session of this program:

						
$ make setsndrcv
gcc -c  -D_GNU_SOURCE -Wall -Wreturn-type setsndrcv.c
gcc setsndrcv.o -o setsndrcv
$ ./setsndrcv
Socket s : 3
 Send buf: 10000 bytes
 Recv buf: 16384 bytes
$

					

Notice the results that were reported by the program! They appear as twice the original sizes that were specified. The reason for this can be found in the Linux kernel source code module /usr/src/linux-2.2.10/net/core/ sock.c. Look for the case statements for SO_SNDBUF and SO_RCVBUF. Here is a code excerpt for the SO_SNDBUF handling within the kernel module sock.c (this particular code segment appears to have been contributed by Alan Cox based on the source code comments at the top of the module):

						
case SO_SNDBUF:
         /* Don't error on this BSD doesn't and if you think
            about it this is right. Otherwise apps have to
            play 'guess the biggest size' games. RCVBUF/SNDBUF
            are treated in BSD as hints */

         if (val > sysctl_wmem_max)
                 val = sysctl_wmem_max;

         sk->sndbuf = max(val*2,2048);

         /*
          *      Wake up sending tasks if we
          *      upped the value.
          */
         sk->write_space(sk);
         break;

					

Based upon the code shown, what actually happens for SO_SNDBUF is this (Linux kernel 2.2.10):

  1. The SO_SNDBUF option value is checked to see whether it exceeds the maximum buffer size.

  2. If the SO_SNDBUF option does exceed the maximum in step 1, the maximum value is used without returning an error to the caller.

  3. The value of 2048 bytes or double the value from steps 1 and 2 is used, whichever value is greater.

The message here is that the option value SO_SNDBUF is only a hint value to be used. The kernel will ultimately decide the best buffer size to apply for SO_SNDBUF.

Examination of more kernel source code reveals something similar for the SO_RCVBUF option. See the following code excerpt (this code segment written by Alan Cox):

						
case SO_RCVBUF:
         /* Don't error on this BSD doesn't and if you think
            about it this is right. Otherwise apps have to
            play 'guess the biggest size' games. RCVBUF/SNDBUF
           are treated in BSD as hints */

         if (val > sysctl_rmem_max)
                val = sysctl_rmem_max;

         /* FIXME: is this lower bound the right one? */
         sk->rcvbuf = max(val*2,256);
         break;

					

For kernel release 2.2.10, the value actually used will be a minimum value of 256 bytes or the given value doubled (unless the given value exceeds the kernel maximum). Again, this emphasizes the fact that these option settings are hints to the kernel, and are not absolute.

CAUTION

Note that setting the SOL_SOCKET options SO_SNDBUF or SO_RCVBUF only provides hints to the kernel from the application. The kernel will ultimately decide the final values that will be established.

If it is critical for the application and kernel to precisely agree on these sizes, the application should retrieve the final values established by the kernel. This is done with a subsequent call to the function getsockopt(2).


< BACKCONTINUE >

Index terms contained in this section

arguments
      setsocketopt(2) function
functions
     setsockopt(2)
            arguments
            cautions 2nd 3rd
            code example 2nd 3rd 4th
            syntax
listings
      setsocketopt(2) function code example 2nd
setsockopt(2) function
      arguments
      cautions 2nd 3rd
      code example 2nd 3rd 4th
      syntax
setting
      socket options 2nd 3rd 4th 5th
sockets
     options
            setting 2nd 3rd 4th 5th
syntax
      setsockopt(2) function