Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223008110133020068229245045188014

Examining the Client Program

The client program mktwatch must bind itself to the broadcast address being used, so that it can receive the broadcast market quotations. Examine the mktwatch.c module shown in Listing 18.5.

Example 18.5. mktwatch.cóThe market Watch Client Program
 <$nopage>
001 1:   /* mktwatch.c:
002 2:    *
003 3:    * Get datagram stock market
004 4:    * quotes from central quotes
005 5:    * server:
006 6:    */
007 7:   #include "quotes.h"
008 8:
009 9:   /*
010 10:   * -b option (broadcast) address:
011 11:   */
012 12:  static char *cmdopt_b = DFLT_BCAST;
013 13:
014 14:  /*
015 15:   * Display command usage:
016 16:   */
017 17:  static void
018 18:  usage(void) { <$nopage>
019 19:      puts("Usage:\tmktwatch [-b bcast]");
020 20:      puts("where:");
021 21:      puts("\t-b bcast\tBroadcast address");
022 22:  }
023 23:
024 24:  /*
025 25:   * Extract ticker information from
026 26:   * broadcast datagram packet:
027 27:   */
028 28:  static int
029 29:  extract(char *dgram,TickReq *tkr) {
030 30:      char *cp = dgram;
031 31:
032 32:      memset(tkr,0,sizeof *tkr);
033 33:      strncpy(tkr->ticker,dgram,TICKLEN)
034 34:          [TICKLEN] = 0;
035 35:      cp += strlen(cp) + 1;
036 36:      if ( sscanf(cp,"%lE",&tkr->last_trade) != 1 )
037 37:          return -1;
038 38:      cp += strlen(cp) + 1;
039 39:      tkr->date = cp;
040 40:      cp += strlen(cp) + 1;
041 41:      tkr->time = cp;
042 42:      cp += strlen(cp) + 1;
043 43:      if ( sscanf(cp,"%lE",&tkr->change) != 1 )
044 44:          return -1;
045 45:      cp += strlen(cp) + 1;
046 46:      if ( sscanf(cp,"%lE",&tkr->open_price) != 1 )
047 47:          return -1;
048 48:      cp += strlen(cp) + 1;
049 49:      if ( sscanf(cp,"%lE",&tkr->high) != 1 )
050 50:          return -1;
051 51:      cp += strlen(cp) + 1;
052 52:      if ( sscanf(cp,"%lE",&tkr->low) != 1 )
053 53:          return -1;
054 54:      cp += strlen(cp) + 1;
055 55:      if ( sscanf(cp,"%lE",&tkr->volume) != 1 )
056 56:          return -1;
057 57:      return 0;
058 58:  }
059 59:
060 60:  /*
061 61:   * Market Watch Main Program:
062 62:   */
063 63:  int
064 64:  main(int argc,char **argv) {
065 65:      int rc = 0;     /* Command return code */
066 66:      int optch;         /* Option character */
067 67:      int z;                  /* Status code */
068 68:      int s;                       /* Socket */
069 69:      socklen_t bc_len;           /* length  */
070 70:      struct sockaddr_in bc_addr; /* AF_INET */
071 71:      socklen_t a_len;     /* Address length */
072 72:      struct sockaddr_in adr;     /* AF_INET */
073 73:      char dgram[2048];       /* Recv buffer */ <$nopage>
074 74:      const int True = TRUE;  /* True const. */
075 75:      TickReq tkr;            /* Ticker Data */
076 76:      const char cmdopts[] = "hb:";
077 77:
078 78:      /*
079 79:       * Parse command line options:
080 80:       */
081 81:      while ( (optch = getopt(argc,argv,cmdopts)) != -1 )
082 82:          switch ( optch ) {
083 83:
084 84:          case 'h' :            /* -h (help) */
085 85:              usage();
086 86:              return rc;
087 87:
088 88:          case 'b' :           /* -b bc_addr */
089 89:              cmdopt_b = optarg;
090 90:              break;
091 91:
092 92:          default :
093 93:              /* Option error */
094 94:              rc = 1;
095 95:      }
096 96:
097 97:      if ( rc ) {
098 98:          usage();          /* Option errors */
099 99:          return rc;
100 100:     }
101 101:
102 102:     /*
103 103:      * Form broadcast address:
104 104:      */
105 105:     bc_len = sizeof bc_addr;
106 106:     z = mkaddr(
107 107:         &bc_addr,   /* Returned addr. */
108 108:         &bc_len,    /* Returned len. */
109 109:         cmdopt_b,   /* Input address */
110 110:         "udp");     /* UDP protocol */  <$nopage>
111 111:
112 112:     if ( z == -1 ) {
113 113:         fprintf(stderr,
114 114:             "%s: -b %s",
115 115:             strerror(errno),
116 116:             cmdopt_b);
117 117:         return 1;
118 118:     }
119 119:
120 120:     /*
121 121:      * Create a UDP socket to read from:
122 122:      */
123 123:     s = socket(PF_INET,SOCK_DGRAM,0);
124 124:     if ( s == -1 ) {
125 125:         fprintf(stderr,
126 126:             "%s: socket(2)\n",
127 127:             strerror(errno));
128 128:         return 1;
129 129:     }
130 130:
131 131:     /*
132 132:      * Allow multiple listeners on this
133 133:      * broadcast address:
134 134:      */
135 135:     z = setsockopt(s,
136 136:         SOL_SOCKET,
137 137:         SO_REUSEADDR,
138 138:         &True,
139 139:         sizeof True);
140 140:
141 141:     if ( z == -1 ) {
142 142:         fprintf(stderr,
143 143:             "%s: setsockopt(SO_REUSEADDR)\n",
144 144:             strerror(errno));
145 145:         return 1;
146 146:     }
147 147:
148 148:     /*
149 149:      * Bind to the broadcast address:
150 150:      */
151 151:     z = bind(s,
152 152:         (struct sockaddr *)&bc_addr,bc_len);
153 153:
154 154:     if ( z == -1 ) {
155 155:         fprintf(stderr,
156 156:             "%s: bind(%s)\n",
157 157:             strerror(errno),
158 158:             cmdopt_b);
159 159:         return 1;
160 160:     }
161 161:
162 162:     /*
163 163:      * Now listen for and process broadcasted
164 164:      * stock quotes:
165 165:      */
166 166:     for (;;) {
167 167:         /*
168 168:          * Wait for a broadcast message:
169 169:          */
170 170:         a_len = sizeof adr; /* Max addr len. */
171 171:         z = recvfrom(s,            /* Socket */
172 172:             dgram,       /* Receiving buffer */ <$nopage>
173 173:             sizeof dgram,/* Max rcv buf size */
174 174:             0,          /* Flags: no options */
175 175:             (struct sockaddr *)&adr, /* Addr */
176 176:             &a_len);   /* Addr len, in & out */
177 177:
178 178:         if ( z < 0 ) {
179 179:             fprintf(stderr,
180 180:                 "%s: recvfrom(2)\n",
181 181:                 strerror(errno));
182 182:             break;
183 183:         }
184 184:
185 185:         /*
186 186:          * Extract and report quote:
187 187:          */
188 188:         if ( !extract(dgram,&tkr) ) {
189 189:             printf("%-*s %7.3f %s %7s %+7.3f %7.3f "
190 190:                 "%7.3f %7.3f %9.0f\n",
191 191:                 TICKLEN,
192 192:                 tkr.ticker,
193 193:                 tkr.last_trade,
194 194:                 tkr.date,
195 195:                 tkr.time,
196 196:                 tkr.change,
197 197:                 tkr.open_price,
198 198:                 tkr.high,
199 199:                 tkr.low,
200 200:                 tkr.volume);
201 201:             fflush(stdout);
202 202:         }
203 203:     }
204 204:
205 205:     return 0;
206 206: }

The interesting features of this program are as follows:

  1. Command-line options are parsed (lines 81 to 100).

  2. A broadcast address is formed (lines 105 to 118).

  3. A datagram (UDP) socket is created (lines 123 to 129).

  4. The SO_REUSEADDR option is enabled (lines 135 to 146). This is essential if more than one client program is to receive the same broadcasts on the same host machine.

  5. The broadcast address is bound (lines 151 to 160).

  6. A client listening for loop begins in line 166. This loop continues forever until the program is terminated (usually with the interrupt character such as Ctrl+C).

After the client begins listening for datagram broadcasts, the following steps are carried out in the for loop:

  1. The program waits for a datagram to arrive (lines 170 to 183). The received datagrams will be the qserve server broadcast messages.

  2. A function named extract() extracts all the string data contained within the datagram and places the converted data into a TickReq structure entry named tkr (line 188). The extract function is defined in lines 28 to 58.

  3. If the datagram extraction is successful, the ticker data is reported to standard output (lines 189 to 201).

  4. Step 1 is repeated until the program is terminated.

Now is the time to put all this code into action!

< BACKCONTINUE >

Index terms contained in this section

arket Watch client program 2nd 3rd 4th 5th 6th
ktwatch client program
      example 2nd 3rd 4th 5th 6th
kwatch client program
lients
      mktwatch program 2nd 3rd 4th 5th 6th 7th
rograms
     clients
            mktwatch 2nd 3rd 4th 5th 6th
tock market quotes
      mkwatch program
uotes (stock market)
      mkwatch client program
xamples
      mktwatch client program 2nd 3rd 4th 5th 6th