Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223008110133020068224205186142182

Examining the Quote Server Program

The logical place to start examining code is the qserve.c source module. This module forms the main program for the quote server itself. It is responsible for obtaining stock market quotes and then broadcasting them to the local area network. Listing 18.1 shows the source listing for qserve.c.

Example 18.1. qserve.cóThe Quote Server Module
 <$nopage>
001 1:   /* qserve.c:
002 2:    *
003 3:    * Stock Quote Concentrator Program:
004 4:    */
005 5:   #include "quotes.h"
006 6: <$nopage>
007 7:   static char *command = NULL;
008 8:
009 9:   /* Remote Quote Server Address */
010 10:  static char *cmdopt_a = DFLT_SERVER;
011 11:
012 12:  /* Quote Re-Broadcast Address */
013 13:  static char *cmdopt_b = DFLT_BCAST;
014 14:
015 15:  /*
016 16:   * Ticker Table:
017 17:   */
018 18:  static TickReq tickers[MAX_TICKERS];
019 19:  static int ntick = 0;
020 20:
021 21:  /*
022 22:   * Return server usage information:
023 23:   */
024 24:  static void
025 25:  usage(void) {
026 26:      printf("Usage: %s [-h] [-a address:port]\n"
027 27:          "where:\n"
028 28:          "\t-h\t\tRequests usage info.\n"
029 29:          "\t-a address:port\tSpecify "
030 30:          "the server\n"
031 31:          "\t\t\taddress and port number.\n"
032 32:          "\t-b bcast:port\tSpecify "
033 33:          "the broadcast\n"
034 34:          "\t\t\taddress and port number.\n",
035 35:          command);
036 36:  }
037 37:
038 38:  /*
039 39:   * Server Main Program:
040 40:   */
041 41:  int
042 42:  main(int argc,char **argv) {
043 43:      int rc = 0;              /* Return Code */
044 44:      int optch;              /* Option Char. */
045 45:      int z;                   /* Status Code */
046 46:      int x;                          /* Index */
047 47:      int s;              /* Broadcast socket */
048 48:      time_t tn = 0;             /* Time Next */
049 49:      time_t zzz;               /* Sleep Time */
050 50:      time_t tm = 20;              /* Seconds */
051 51:      time_t td;               /* Time & Date */
052 52:      struct sockaddr_in bc_addr; /* bc addr */
053 53:      socklen_t bc_len;      /* bc addr len. */
054 54:      const int True = TRUE;  /* Const. TRUE */
055 55:      static char cmdopts[] = "ha:b:";
056 56:
057 57:      /*
058 58:       * Process command line options:
059 59:       */
060 60:      command = Basename(argv[0]);
061 61:
062 62:      while ( (optch = getopt(argc,argv,cmdopts)) != -1 )
063 63:          switch ( optch ) {
064 64:
065 65:          case 'h' :          /* -h for help */
066 66:              usage();
067 67:              return rc;
068 68:
069 69:          case 'a' :      /* -a quote_server */
070 70:              cmdopt_a = optarg;
071 71:              break;
072 72:
073 73:          case 'b' :    /* -b broadcast_addr */
074 74:              cmdopt_b = optarg;
075 75:              break;
076 76:
077 77:          default :
078 78:              /* Option error */
079 79:              rc = 1;
080 80:      }
081 81:
082 82:      /*
083 83:       * Check for option errors:
084 84:       */
085 85:      if ( rc ) {
086 86:          usage();
087 87:          return rc;
088 88:      }
089 89:
090 90:      /*
091 91:       * Form the broadcast server
092 92:       * address:
093 93:       */
094 94:      bc_len = sizeof bc_addr;    /* Max len */
095 95:      z = mkaddr(
096 96:          &bc_addr,        /* Returned addr. */
097 97:          &bc_len,          /* Returned len. */
098 98:          cmdopt_b,         /* Input address */ <$nopage>
099 99:          "udp");            /* UDP protocol */
100 100:
101 101:     if ( z == -1 ) {
102 102:         msgf('e',"%s: -b %s",
103 103:             strerror(errno),
104 104:             cmdopt_b);  <$nopage>
105 105:         return 1;
106 106:     }
107 107:
108 108:     /*
109 109:      * Create a UDP socket to use:
110 110:      */
111 111:     s = socket(PF_INET,SOCK_DGRAM,0);
112 112:
113 113:     if ( s == -1 ) {
114 114:         msgf('e',"%s: socket(PF_INET,"
115 115:             "SOCK_DGRAM,0)",
116 116:             strerror(errno));
117 117:         return 1;
118 118:     }
119 119:
120 120:     /*
121 121:      * Allow broadcasts on socket s:
122 122:      */
123 123:     z = setsockopt(s,
124 124:         SOL_SOCKET,
125 125:         SO_BROADCAST,
126 126:         &True,
127 127:         sizeof True);
128 128:
129 129:     if ( z == -1 ) {
130 130:         msgf('e',"%s: setsockopt(SO_BROADCAST)",
131 131:             strerror(errno));
132 132:         return 1;
133 133:     }
134 134:
135 135:     /*
136 136:      * Load tickers from tickers.rc:
137 137:      */
138 138:     if ( load(&tickers[0],&ntick,MAX_TICKERS) )
139 139:         goto errxit;
140 140:
141 141:     /*
142 142:      * Now monitor the remote quote server:
143 143:      */
144 144:     for (;;) {
145 145:         tn = 0;              /* Refresh tn */
146 146:         time(&td);         /* Current time */
147 147:
148 148:         /*
149 149:          * Loop for all tickers:
150 150:          */
151 151:         for ( x=0; x<ntick; ++x ) {
152 152:             /*
153 153:              * Skip tickers that are either
154 154:              * unknown, or are producing parse
155 155:              * errors in the returned data:
156 156:              */
157 157:             if ( tickers[x].flags & FLG_UNKNOWN
158 158:               || tickers[x].flags & FLG_ERROR )
159 159:                 continue;   /* Ignore this */
160 160:
161 161:             /*
162 162:              * Pick up the earliest "next" time:
163 163:              */
164 164:             if ( !tn
165 165:               || tickers[x].next_samp < tn )
166 166:                 tn = tickers[x].next_samp;
167 167:
168 168:             /*
169 169:              * If the current time is > than
170 170:              * the "next" time, it is time to
171 171:              * fetch an update for this ticker:
172 172:              */
173 173:             if ( td >= tickers[x].next_samp ) {
174 174:                 /*
175 175:                  * Get Quote Update:
176 176:                  */
177 177:                 z = get_tickinfo(
178 178:                     &tickers[x],cmdopt_a);
179 179:
180 180:                 /*
181 181:                  * Compute time for the next
182 182:                  * update for this ticker:
183 183:                  */
184 184:                 time(&tickers[x].next_samp);
185 185:                 tickers[x].next_samp += tm;
186 186:
187 187:                 /*
188 188:                  * If the quote fetch was OK,
189 189:                  * then broadcast its info:
190 190:                  */ <$nopage>
191 191:                 if ( !z )
192 192:                     broadcast(s,&tickers[x],
193 193:                         (struct sockaddr *)&bc_addr,
194 194:                         bc_len);
195 195:             }
196 196:         }
197 197:
198 198:         /*
199 199:          * Here the interval between updates is
200 200:          * progressively increased to 5 minutes
201 201:          * max. This provides a lot of initial
202 202:          * action for demonstration purposes,
203 203:          * without taxing the friendly quote
204 204:          * providers if this program is run all
205 205:          * day. Abuse will only force the kind
206 206:          * providers to change things to break
207 207:          * the operation of this program!
208 208:          */
209 209:         if ( tm < (time_t) 5 * 60 )
210 210:             tm += 5;    /* Progressively increase */
211 211:
212 212:         /*
213 213:          * Compute how long we need to snooze.
214 214:          * The time to the next event is
215 215:          * computed- sleep(3) is called if
216 216:          * necessary:
217 217:          */
218 218:         if ( !tn )
219 219:             tn = td + tm;
220 220:         if ( tn >= td )
221 221:             if ( (zzz = tn - td) )
222 222:                 sleep(zzz);
223 223:     }
224 224:
225 225:     return rc;
226 226:
227 227:     /*
228 228:      * Error Exit:
229 229:      */
230 230: errxit:
231 231:     return rc = 2;
232 232: }

Note the following highlights about the program organization:

  • This program accepts the -a or -b options, which are stored in variables cmdopt_a and cmdopt_b, respectively (lines 9 to 13).

  • The stock market tickers to be monitored are maintained in the table tickers[] (line 18). The variable ntick indicates how many active entries are in the table (line 19).

  • Function usage() provides usage information upon request when option -h is provided (lines 24 to 36).

  • The remainder of the program is the main program for the server (lines 41 to the end).

Now, examine the flow of control in the server main program:

  1. Options are parsed in a getopt(3) loop (lines 62 to 88).

  2. The broadcast address is formed in bc_addr by calling upon the mkaddr() function (lines 94 to 106).

  3. A UDP socket is created by calling socket(2) (lines 111 to 118).

  4. Enable the broadcast feature of the socket from step 3 (lines 123 to 133).

  5. Call upon the load() function to load the tickers[] table from the initialization file tickers.rc (lines 138 and 139).

  6. The server then executes an infinite server loop until the program is terminated (lines 144 to 233).

Now, examine the server loop steps that are used:

  1. The "next time" value tn is cleared to zero (line 145). The current time is also placed into td (line 146).

  2. A for loop in line 151 iterates through all ticker table entries (lines 151 to 196). This loop will later be described separately.

  3. The value of tm represents the time to pause between ticker updates. It was initialized to a value of 20 (seconds) in line 50. In line 209, it is tested to see whether the value is greater than five minutes. If not, tm has five more seconds added to it (line 210). This is done so that the time interval will increase gradually (to a maximum of five minutes), in case the server is left running all day. This will prevent abuse of the Yahoo! quotation servers, which are kindly providing a free service to you.

  4. If an event time is found in tn, the amount of time to sleep is computed and placed into variable zzz and sleep(3) is called. Otherwise, the loop immediately begins another iteration.

Now examine the more interesting for loop beginning in line 151:

  1. The ticker table entries contain a flags member. If the flag bit FLG_UNKNOWN is set (line 157), this indicates that the ticker has been discovered to be unknown. After this bit is set, the ticker is never looked up again (continue in line 159 causes it to be ignored). Likewise, if flag FLG_ERROR is set (line 158), the ticker is not looked up again. This flag indicates that a data format error was encountered while trying to decode the quotation.

  2. The current time and date in td are compared with the next event time for the ticker entry ticker[x] (line 173). The next event time is stored in member next_samp, which indicates when the next sample should be taken. If the current time is greater than or equal to the next sample event time, then it is time to fetch a new quote for this ticker.

  3. The function get_tickinfo() is called to obtain ticker information for this ticker symbol (lines 177 to 178).

  4. A next event time is computed from taking the current time and adding the time period tm to it (which increases to a maximum of five minutes). This is done in lines 184 to 185.

  5. A test is made to see whether the ticker quote fetch was successful (line 191). If it was, the function broadcast() is called in lines 192 to 194 to send the information out to all interested local area network client programs.

  6. Repeat step 1, increasing x, until all ticker symbols have been processed in tickers[].

That covers the operation of the main segment of the server code. The next sections will cover the operation of the quotation fetch and then the broadcast function.

< BACKCONTINUE >

Index terms contained in this section

etching
      stock market quotes 2nd 3rd 4th 5th 6th 7th
examples
      Quote Server module
ownloading
      stock quotes 2nd 3rd 4th 5th 6th 7th
roblems
      quote service example 2nd 3rd 4th 5th 6th 7th
rograms
      Quote Server
tock market quotes
      obtaining 2nd 3rd 4th 5th 6th 7th
uote Server program
      example 2nd 3rd 4th 5th 6th 7th
uote service problem example 2nd 3rd 4th 5th 6th 7th
uotes (stock market) 2nd 3rd 4th 5th 6th 7th
      fetch procedure
      fetch proceedure 2nd 3rd 4th 5th 6th
      Quote Server program
xamples
      Quote Server module 2nd 3rd 4th 5th 6th
      quote service problem 2nd 3rd 4th 5th 6th 7th