Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223008110133020068226224151164022

Fetching Quotations via get_tickinfo()

This section will examine the source module gettick.c so that you can see how the quotation was retrieved by the C code. Before that module can be shown, however, you need to examine some of the structure references that are being used. Listing 18.2 shows the quotes.h header file used by the source modules in this project.

Example 18.2. quotes.hThe quotes.h Header File
 <$nopage>
001 1:   /* quotes.h:
002 2:    *
003 3:    * Project header file:
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 <ctype.h>
010 10:  #include <string.h>
011 11:  #include <getopt.h>
012 12:  #include <memory.h>
013 13:  #include <stdarg.h>
014 14:  #include <math.h>
015 15:  #include <syslog.h>
016 16:  #include <signal.h>
017 17:  #include <sys/types.h>
018 18:  #include <sys/time.h>
019 19:  #include <sys/socket.h>
020 20:  #include <netinet/in.h>
021 21:
022 22:  /*
023 23:   * Default Quote Server:
024 24:   */
025 25:  #define DFLT_SERVER "finance.yahoo.com:80"
026 26:
027 27:  /*
028 28:   * Default Broadcast Address:
029 29:   */
030 30:  #define DFLT_BCAST  "127.255.255.255:9777"
031 31:
032 32:  /*
033 33:   * *.CSV Parsing Parameter:
034 34:   */
035 35:  typedef struct {
036 36:      char    type;       /* 'S' or 'D' */
037 37:      void    *parm;      /* Ptr to parameter */
038 38:  } Parm;
039 39:
040 40:  /*
041 41:   * Timeout on Quote Fetch:
042 42:   */
043 43:  #define TIMEOUT_SECS    10
044 44:
045 45:  /*
046 46:   * Ticker load file:
047 47:   */
048 48:  #define TICKPATH        "tickers.rc"
049 49:
050 50:  /*
051 51:   * Maximum number of tickers:
052 52:   */ <$nopage>
053 53:  #define MAX_TICKERS     256
054 54:
055 55:  /*
056 56:   * Ticker length:
057 57:   */
058 58:  #define TICKLEN         8
059 59:
060 60:  /*
061 61:   * Date Length:
062 62:   */
063 63:  #define DTLEN           10
064 64:
065 65:  /*
066 66:   * Time field length:
067 67:   */
068 68:  #define TMLEN           7
069 69:
070 70:  /*
071 71:   * Define TRUE & FALSE if not defined:
072 72:   */
073 73:  #ifndef TRUE
074 74:  #define TRUE    1
075 75:  #define FALSE   0
076 76:  #endif
077 77:
078 78:  /*
079 79:   * Ticker Request Structure:
080 80:   */
081 81:  typedef struct {
082 82:      char    ticker[TICKLEN+1];   /* Symbol */
083 83:      double  last_trade;      /* Last Price */
084 84:      char    *date;                  /* Date */
085 85:      char    *time;   /* Time of Last Trade */
086 86:      double  change;          /* +/- Change */
087 87:      double  open_price;   /* Opening Price */
088 88:      double  high;             /* High Price */
089 89:      double  low;               /* Low Price */
090 90:      double  volume;    /* Volume of Trades */
091 91:      int     flags;          /* Server flags */
092 92:      time_t  next_samp; /* Time of next evt */
093 93:  } TickReq;
094 94:
095 95:  /*
096 96:   * Ticker Flags:
097 97:   */
098 98:           /* Ticker unknown */
099 99:  #define FLG_UNKNOWN     1
100 100:          /* Data format error */
101 101: #define FLG_ERROR       2
102 102:
103 103: /*
104 104:  * External Function References:
105 105:  */
106 106: extern int load(
107 107:     TickReq *tick,int *pntick,int nmax);
108 108: extern int extract_parms(
109 109:     Parm *plist,short n,char *src);
110 110: extern void msgf(
111 111:     char type,const char *format,);
112 112: extern int Connect(const char *addr);
113 113: extern int mkaddr(
114 114:     void *addr,
115 115:     int *addrlen,
116 116:     char *str_addr,
117 117:     char *protocol);
118 118: extern char *Basename(char *cmd);
119 119: extern char *strtick(char *str);
120 120: extern int get_tickinfo(TickReq *req,char *addr);
121 121: extern void broadcast(
122 122:     int s,TickReq *quote,struct sockaddr *bc_addr,
123 123:     socklen_t bc_len);
124 124
125 125: /* End */
126  <$nopage>

The items that are of primary interest to you are

  • Macro DFLT_SERVER (line 25) defines the default hostname and port to contact for quotes (note that non-Yahoo! servers will likely use different spreadsheet data formats).

  • Macro DFLT_BCAST (line 30) defines the default broadcast address. The default is to broadcast to the loopback network so that it will work for those readers that do not have a network card installed.

  • Structure Parm (lines 35 to 38) defines a type that controls parsing of information into a C data type.

  • Macro TIMEOUT_SECS is set to 10 seconds, and defines the maximum amount of time to wait for a stock quotation before giving up on a response (line 48).

  • Macro MAX_TICKERS defines the table size of tickers[] in the module qserve.c (line 53).

  • Structure TickReq (lines 81 to 93) defines one ticker symbol entry for the server. This structure is also used in the client program, but structure members flags and next_samp are ignored by the client program.

  • Flags FLG_UNKNOWN and FLG_ERROR are defined in lines 99 and 101.

With the header file out of the way, examine Listing 18.3, which illustrates the gettick.c source module.

Example 18.3. gettick.cThe get_tickinfo() Function Source Code
 <$nopage>
001 1:   /* gettick.c
002 2:    *
003 3:    * Get ticker info from inet:
004 4:    */
005 5:   #include "quotes.h"
006 6:
007 7:   /*
008 8:    * f is set TRUE when a request
009 9:    * for a stock quote has timed
010 10:   * out.
011 11:   */
012 12:  static int f = FALSE;
013 13:
014 14:  /*
015 15:   * Catch SIGALRM and Timeout:
016 16:   */
017 17:  static void
018 18:  sig_ALRM(int signo) {
019 19:      f = TRUE;   /* Mark timeout */
020 20:  }
021 21:
022 22:  /*
023 23:   * Get ticker info:
024 24:   *
025 25:   * RETURNS:
026 26:   *  0       Success
027 27:   *  -1      Failed:
028 28:   *
029 29:   * errno:
030 30:   *  ETIME   Timed Out
031 31:   *  EBADMSG Field data format
032 32:   *  other   Network/system errors.
033 33:   */
034 34:  int
035 35:  get_tickinfo(TickReq *req,char *addr) {
036 36:      int z, er;           /* Status, errno */
037 37:      int s;               /* Socket */
038 38:      int n;               /* Byte count */
039 39:      char buf[256];      /* Receive buffer */
040 40:      char *tkr = NULL;   /* Extracted ticker */
041 41:      struct sigaction
042 42:          sa_new,          /* New signal action */
043 43:          sa_old;          /* Saved signal action */
044 44:      Parm parms[9];      /* Data parse table */
045 45:
046 46:      /*
047 47:       * Initialize parsing parameters. This
048 48:       * parameter list will need modification
049 49:       * if yahoo or your quote provider uses
050 50:       * a different format:
051 51:       */
052 52:      parms[0].type = 'S';    /* String */
053 53:      parms[0].parm = &tkr;   /* Ticker name */
054 54:      parms[1].type = 'D';    /* Double */
055 55:      parms[1].parm = &req->last_trade;
056 56:      parms[2].type = 'S';
057 57:      parms[2].parm = &req->date;
058 58:      parms[3].type = 'S';
059 59:      parms[3].parm = &req->time;
060 60:      parms[4].type = 'D';
061 61:      parms[4].parm = &req->change;
062 62:      parms[5].type = 'D';
063 63:      parms[5].parm = &req->open_price;
064 64:      parms[6].type = 'D';
065 65:      parms[6].parm = &req->high;
066 66:      parms[7].type = 'D';
067 67:      parms[7].parm = &req->low;
068 68:      parms[8].type = 'D';
069 69:      parms[8].parm = &req->volume;
070 70:
071 71:      /*
072 72:       * Initialize to catch SIGALRM:
073 73:       */
074 74:      sa_new.sa_handler = sig_ALRM;
075 75:      sigemptyset(&sa_new.sa_mask);
076 76:      sa_new.sa_flags = 0;
077 77:      sigaction(SIGALRM,&sa_new,&sa_old);
078 78:
079 79:      /*
080 80:       * Connect to finance.yahoo.com:
081 81:       */  <$nopage>
082 82:      f = FALSE;
083 83:      alarm(TIMEOUT_SECS);
084 84:
085 85:      s = Connect(addr);
086 86:      if ( s == -1 )
087 87:          goto errxit;
088 88:
089 89:      /*
090 90:       * Send GET request:
091 91:       *
092 92:       * NOTE: This is subject to change-
093 93:       * If finance.yahoo.com changes, you
094 94:       * will need to adjust this formatting.
095 95:       */
096 96:      sprintf(buf,"GET /d/quotes.csv?"
097 97:          "s=%s"
098 98:          "&f=sl1d1t1c1ohgv"
099 99:          "&e=.csv\r\n",
100 100:         req->ticker);
101 101:
102 102:     write(s,buf,strlen(buf));
103 103:     shutdown(s,1);
104 104:
105 105:     /*
106 106:      * Read response with a timeout:
107 107:      */
108 108:     do  {
109 109:         z = read(s,buf,sizeof buf);
110 110:     } while ( !f && z == -1 && errno == EINTR );
111 111:
112 112:     er = errno;              /* Save error */
113 113:     alarm(0);           /* Disable timeout */
114 114:     close(s);              /* Close socket */
115 115:
116 116:     /* Restore the signal action */
117 117:     sigaction(SIGALRM,&sa_old,NULL);  <$nopage>
118 118:
119 119:     if ( !f && z > 0 )
120 120:         n = z;          /* Read n bytes OK */
121 121:     else {
122 122:         if ( f )               /* Timeout? */
123 123:             er = ETIME;    /* Yes- timeout */
124 124:         /*
125 125:          * Report error to log:
126 126:          */
127 127:         msgf('e',"%s: Get ticker '%s'",
128 128:             strerror(er),
129 129:             req->ticker);
130 130:
131 131:         errno = er;          /* For caller */
132 132:         return -1;               /* Failed */
133 133:     }
134 134:
135 135:     /* Remove CR, LF, or CRLF */
136 136:     buf[strcspn(buf,"\r\n")] = 0;
137 137:
138 138:     /*
139 139:      * Check for the unknown ticker case:
140 140:      */
141 141:     if ( strstr(buf,"N/A,N/A,N/A,N/A,N/A") ) {
142 142:         msgf('e',"Unknown Ticker: '%s'",
143 143:             req->ticker);
144 144:         req->flags |= FLG_UNKNOWN;
145 145:         errno = EBADMSG;    /* For caller */
146 146:         return -1;          /* Failed */
147 147:     }
148 148:
149 149:     /*
150 150:      * Parse quote results:
151 151:      */
152 152:     if ( (z = extract_parms(parms,9,buf)) < 0 ) {
153 153:         /* Report failed parse of data */
154 154:         msgf('e',"Field # %d: '%s'",z,buf);
155 155:         req->flags |= FLG_ERROR;
156 156:         errno = EBADMSG;    /* For caller */
157 157:         return -1;          /* Failed */
158 158:     }
159 159:
160 160:     /* Capture the exact case for this ticker */
161 161:     strncpy(req->ticker,tkr,TICKLEN)[TICKLEN] = 0;
162 162:
163 163:     /*
164 164:      * Update sample time in entry:
165 165:      */
166 166:     return 0;
167 167:
168 168:     /*
169 169:      * Error Exit:
170 170:      */
171 171: errxit:
172 172:     alarm(0);
173 173:     sigaction(SIGALRM,&sa_old,NULL);
174 174:     return -1;
175 175: }

This module defines the following major components:

  • The timeout flag f in line 12. When this flag is set TRUE, it will indicate that the request has timed out.

  • The signal catcher function sig_ALRM() that will catch the signal SIGALRM when the timer has expired. This function sets flag variable f to TRUE in line 19.

  • The get_tickinfo() function occupies the remainder of the module, starting in line 34.

Now, examine the procedure used by get_tickinfo():

  1. The data that this function hopes to receive is in spreadsheet format. To parse the data out of this formatted record, a parameter table is established in array parms[] (lines 52 to 69). Type 'S' indicates a string type of data, whereas the 'D' represents a C-type double value to be extracted.

  2. The signal handler for SIGALRM is established (lines 74 to 77).

  3. The flag f is initialized to FALSE and the timer is started (lines 82 and 83).

  4. A connect request is issued by calling function Connect() with the address of our quotation server (lines 85 to 87). The source code for Connect() is provided in module connect.c.

  5. A GET request is formatted (lines 96 to 100). This is one area of code you might need to change if the Yahoo! servers change.

  6. The GET request is written out to the quotation server (lines 102 and 103). The call to shutdown(2) sends and end-file notification to the remote server without closing the socket. This is necessary so that the response can be received from the server.

  7. Wait for and read the resulting spreadsheet record (lines 108 to 110).

  8. Save the errno value in variable er in case an error has occurred (line 112).

  9. Cancel the timer (line 113) and close the socket s (line 114).

  10. Restore the signal handler for signal SIGALRM (line 117).

  11. If flag f is still FALSE and z is greater than zero, then the program successfully received some quotation data (lines 119 and 120). The number of bytes received is recorded in variable n.

  12. Otherwise, if f is TRUE, then set the saved error value in er to ETIME to indicate that a timeout occurred (line 123).

  13. Lines 127 to 132 handle the timeout or error case when a quotation was not successfully received. The call to msgf() logs an error message to the syslog logging facility.

  14. The carriage return or linefeed is removed from the received spreadsheet record (line 136).

  15. A special sequence is tested for, which indicates that the ticker symbol is not known (lines 141 to 147). Yahoo! returns N/A for a number of fields if the ticker symbol is not known.

  16. If the ticker symbol is known, then the spreadsheet record is parsed in lines 152 to 158. If a record parse or field data error occurs, the error exit is taken in line 157.

  17. If the parse is successful, the ticker symbol itself is copied back into the entry req->ticker (line 161). This is done for some symbols that use a mixed case.

  18. Finally, at long last, the return statement in line 166 indicates that the req member has been updated with new ticker information successfully.

The next section will examine the broadcast() function that was called upon by the server program.

< BACKCONTINUE >

Index terms contained in this section

et tickinfo() function 2nd
      example 2nd 3rd 4th 5th
tock market quotes
      get tickinfo() function 2nd 3rd 4th 5th 6th 7th
unctions
      get tickinfo() 2nd 3rd 4th 5th 6th 7th
uotes (stock market)
      get tickinfo() function 2nd 3rd 4th 5th 6th 7th
      quotes.h header file 2nd 3rd
uotes.h header file
      example 2nd 3rd
xamining
      get tickinfo() function
xamples
      get tickinfo() function 2nd 3rd 4th 5th
      quotes.h header file 2nd 3rd