Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Examples  

sockets.c

Go to the documentation of this file.
00001 /*! \file sockets.c
00002     This file implements basic TCP/IP functionalities.
00003  */ 
00004 
00005 #include <sys/wait.h> 
00006 #include <sys/ipc.h>
00007 #include <sys/msg.h>
00008 #include <sys/time.h>
00009 
00010 #include <errno.h>
00011 #include <stdio.h>
00012 #include "my_sockets.h"
00013 
00014 
00015 
00016 
00017 /*! \brief Open a TCP connexion to a remote server.
00018     \param tcp_add Pointer to a structure that will receive the server's IP address. You probably don't need it, but sometimes
00019            you must have it.
00020     \param tcp_host Pointer to a zero terminated string of characters that contains the name (or IP address) of the server
00021            running the host.
00022     \param tcp_port TCP port used by the server to a listen incoming connections.
00023     \param tt_sec Number of second for the timeout.
00024     \param tt_micro Number of micro second for the timeout.
00025     \param ellapsed_time Ellapsed time - in milli seconds - for the connect() system call.
00026     \return If the connection succed, the function returns the newly created socket descriptor (which is a positive value).
00027             Otherwise, if the connection failed, the function returns one of the following codes (negative value):
00028             <UL>
00029               <li>SCK_CREATE_ERROR: Can not create the socket (the function socket() failed).
00030               <li>SCK_CONNECT_ERROR: Can not connect to the remote server (the function connect() failed).
00031               <li>SCK_GET_IP_ERROR: Can not get the remote server's IP address, the function gethostbyname() failed.
00032               <li>SCK_TIMEOUT_CONNECT: The connect() call has been broken by a signal - timeout.
00033               <li>SCK_TIMER_ERROR: An error occured with the high precision timer.
00034               <li>SCK_CHRONO_ERROR: An error occured with the high precision chronometer.
00035             </UL>
00036      \warning Please note that:
00037               <UL>
00038                  <li>To use this function you <B>MUST</B> set a handler on the signal SIGALRM. The signal SIGALRM is used to implement the
00039                      connect() timeout. 
00040                  <li>This use its own timers, so you can still use the timing library that comes with the Common C Library.
00041               </UL>
00042  */
00043 
00044 int open_tcp_connexion (struct sockaddr_in *tcp_add, char *tcp_host, int tcp_port, long tt_sec, long tt_micro, unsigned long int* ellapsed_time)
00045 {
00046   int                   desc;
00047   int                   cr;
00048   struct hostent        *name;
00049   char                  ip[16];
00050   int                   err;
00051   struct timeval        start, stop;
00052   struct itimerval      one_shot_timer, old_one_shot_timer;
00053 
00054   *ellapsed_time = 0;
00055 
00056   /* --------------------------------------------------------------------- */
00057   /* Get the remote server's IP address (using DNS)                        */
00058   /* --------------------------------------------------------------------- */
00059 
00060   name = gethostbyname (tcp_host);
00061   if (name == NULL) { return SCK_GET_IP_ERROR; }
00062 
00063   sprintf (ip, "%d.%d.%d.%d", (unsigned char)(name->h_addr_list)[0][0],
00064                               (unsigned char)(name->h_addr_list)[0][1],
00065                               (unsigned char)(name->h_addr_list)[0][2],
00066                               (unsigned char)(name->h_addr_list)[0][3]);
00067 
00068   /* --------------------------------------------------------------------- */
00069   /* Create client's socket                                                */
00070   /* --------------------------------------------------------------------- */
00071 
00072   desc = socket(AF_INET, SOCK_STREAM, 0);
00073   if (desc == -1) { return (SCK_CREATE_ERROR); };
00074   Get_Sock_Adress (ip, tcp_port, tcp_add);
00075 
00076   /* --------------------------------------------------------------------- */
00077   /* Get the current timestamp                                             */
00078   /* --------------------------------------------------------------------- */
00079 
00080   if (gettimeofday(&start, NULL) == -1) { close(desc); return SCK_CHRONO_ERROR; }
00081 
00082   /* --------------------------------------------------------------------- */
00083   /* Set the one shot timer (that raises SIGALRM)                          */
00084   /* --------------------------------------------------------------------- */
00085 
00086   one_shot_timer.it_interval.tv_sec   = 0;
00087   one_shot_timer.it_interval.tv_usec  = 0;
00088   one_shot_timer.it_value.tv_sec      = tt_sec;
00089   one_shot_timer.it_value.tv_usec     = tt_micro;
00090   if (setitimer (ITIMER_REAL, &one_shot_timer, &old_one_shot_timer) == -1) { close(desc); return SCK_TIMER_ERROR; }
00091 
00092   /* --------------------------------------------------------------------- */
00093   /* Connect to the remote server                                          */
00094   /* --------------------------------------------------------------------- */
00095 
00096   cr  = connect(desc, (struct sockaddr*)tcp_add, sizeof(struct sockaddr_in));
00097   err = errno;
00098 
00099   /* --------------------------------------------------------------------- */
00100   /* Cancel the one shot timer                                             */
00101   /* --------------------------------------------------------------------- */
00102 
00103   one_shot_timer.it_interval.tv_sec   = 0;
00104   one_shot_timer.it_interval.tv_usec  = 0;
00105   one_shot_timer.it_value.tv_sec      = 0;
00106   one_shot_timer.it_value.tv_usec     = 0;
00107   if (setitimer (ITIMER_REAL, &one_shot_timer, &old_one_shot_timer) == -1) { close(desc); return SCK_TIMER_ERROR; }
00108 
00109   /* --------------------------------------------------------------------- */
00110   /* Get the current timestamp                                             */
00111   /* --------------------------------------------------------------------- */
00112 
00113   if (gettimeofday(&stop, NULL) == -1) { close(desc); return SCK_CHRONO_ERROR; }
00114   
00115   if (cr == -1)
00116   {
00117     close(desc);
00118     if (err == EINTR) { return SCK_TIMEOUT_CONNECT; } 
00119     return SCK_CONNECT_ERROR;
00120   } 
00121 
00122   /* --------------------------------------------------------------------- */
00123   /* Calculate the ellapsed time and return                                */
00124   /* --------------------------------------------------------------------- */
00125 
00126   *ellapsed_time = (stop.tv_sec*1000 + stop.tv_usec/1000) - (start.tv_sec*1000 + start.tv_usec/1000);
00127   return (desc);
00128 };
00129 
00130 /*! \example test_sockets.c
00131     This file shows hos to use the function open_tcp_connexion().
00132  */
00133 
00134 /*! \brief Get the Internet address of a service.
00135     \param Machine_serveur Host IP address.
00136     \param port_serveur TCP port.
00137     \param TCP_address Pointer to a "sockaddr_in" used to store the internet address.
00138     \return The function always return SCK_OK.
00139  */
00140 
00141 void Get_Sock_Adress (char *Machine_serveur, int port_serveur, struct sockaddr_in *TCP_address) 
00142 {
00143   memset ((void*)TCP_address, 0, sizeof(struct sockaddr_in));
00144   TCP_address->sin_family        = AF_INET;
00145   TCP_address->sin_port          = htons((u_short)port_serveur);
00146   (TCP_address->sin_addr).s_addr = inet_addr(Machine_serveur);
00147 }
00148 
00149 /*! \brief Read data from an open socket descriptor (read timeout in micro seconds + ellapsed time in milli seconds).
00150     \param sockfd Socket descriptor (this must be a descriptor to an open socket).
00151     \param buffer Pointer to that memory location that will receive the data extracted from the socket.
00152     \param buffer_size Size of the memory location pointed by <i>buffer</i> (in other words, the maximum amount of bytes that can be read).
00153     \param extracted Pointer to an interger that will be used to store the number of extracted bytes.
00154     \param tt_sec Number of seconds of the timeout.
00155     \param tt_micro Number of micro seconds of the timeout.
00156     \param ellapsed_time Pointer to an unsigned long integer that will be used to store the ellapsed time.
00157     \return The function may return one of the following values:
00158             <UL>
00159               <li>SCK_READ_CHRONO_ERROR: an error occured while manipulating the high precesion chronometer.
00160               <li>SCK_READ_TIMER_ERROR: an error occured while manipulating the high precesion timer.
00161               <li>SCK_READ_TIMEOUT: Read timeout.
00162               <li>SCK_READ_READ_ERROR: The system call <i>read()</i> failed.
00163               <li>SCK_READ_OK: sucess.
00164             </UL>
00165  */
00166 
00167 int read_from_socket (int sockfd, char *buffer, size_t buffer_size, size_t* extracted, long tt_sec, long tt_micro, unsigned long int* ellapsed_time)
00168 {
00169   int                   err;
00170   size_t                cr;
00171   struct timeval        start, stop;
00172   struct itimerval      one_shot_timer, old_one_shot_timer;
00173 
00174 
00175   *extracted     = -1;
00176   *ellapsed_time = -1;
00177 
00178   /* --------------------------------------------------------------------- */
00179   /* Get the current timestamp                                             */
00180   /* --------------------------------------------------------------------- */
00181 
00182   if (gettimeofday(&start, NULL) == -1) { return SCK_READ_CHRONO_ERROR; }
00183 
00184   /* --------------------------------------------------------------------- */
00185   /* Set the one shot timer (that raises SIGALRM)                          */
00186   /* --------------------------------------------------------------------- */
00187 
00188   one_shot_timer.it_interval.tv_sec   = 0;
00189   one_shot_timer.it_interval.tv_usec  = 0;
00190   one_shot_timer.it_value.tv_sec      = tt_sec;
00191   one_shot_timer.it_value.tv_usec     = tt_micro;
00192   if (setitimer (ITIMER_REAL, &one_shot_timer, &old_one_shot_timer) == -1) { return SCK_READ_TIMER_ERROR; }
00193 
00194   /* --------------------------------------------------------------------- */
00195   /* Read data from socket                                                 */
00196   /* --------------------------------------------------------------------- */
00197 
00198   cr = read (sockfd, (void*)buffer, buffer_size);
00199   err = errno;
00200 
00201   /* --------------------------------------------------------------------- */
00202   /* Cancel the one shot timer                                             */
00203   /* --------------------------------------------------------------------- */
00204 
00205   one_shot_timer.it_interval.tv_sec   = 0;
00206   one_shot_timer.it_interval.tv_usec  = 0;
00207   one_shot_timer.it_value.tv_sec      = 0;
00208   one_shot_timer.it_value.tv_usec     = 0;
00209   if (setitimer (ITIMER_REAL, &one_shot_timer, &old_one_shot_timer) == -1) { return SCK_READ_TIMER_ERROR; }
00210 
00211   /* --------------------------------------------------------------------- */
00212   /* Get the current timestamp                                             */
00213   /* --------------------------------------------------------------------- */
00214 
00215   if (gettimeofday(&stop, NULL) == -1) { return SCK_READ_CHRONO_ERROR; }
00216 
00217   if (cr == (size_t)-1) 
00218   { 
00219     if (err == EINTR) { return SCK_READ_TIMEOUT; }
00220     return SCK_READ_READ_ERROR;
00221   }
00222 
00223   /* --------------------------------------------------------------------- */
00224   /* Calculate ellapsed time in milli seconds and exit                     */
00225   /* --------------------------------------------------------------------- */
00226 
00227   *ellapsed_time = (stop.tv_sec*1000 + stop.tv_usec/1000) - (start.tv_sec*1000 + start.tv_usec/1000);
00228   *extracted = cr;
00229 
00230   return SCK_READ_OK;
00231 }
00232 
00233 /*! \brief Read from a socket, until the connexion is closed.
00234     \param sockfd Socket descriptor.
00235     \param buffer Pointer to a pointer that will be used to point to a dynamically allocated memory space. This memory space will contain
00236                   a zero terminated string of characters which is the peer's reponse.
00237     \param tt_sec Number of seconds for the timeout.
00238     \param tt_micro Number of micro seconds for the timeout.
00239     \param ellapsed_time Pointer to an <i>unsigned long int</i> that will be used to store the ellapsed time.
00240     \param size Pointer to an <i>unsigned long int</i> that will be used to store the number of bytes read from the peer.
00241     \return The function may return the following values:
00242             <UL>
00243               <li>SCK_READ_OK: the action was succesful. <i>buffer</i> points to a zero terminated string of characters.
00244               <li>SCK_READ_NO_MEM: the function could not allocate memory.
00245               <li>SCK_READ_READ_ERROR: The system call read() failed.
00246               <li>SCK_READ_TIMEOUT: Read timeout.
00247               <li>SCK_READ_CHRONO_ERROR: A problem occured with the high precision chronometer.
00248               <li>SCK_READ_TIMER_ERROR: A problem occured with the high precision timer.
00249             </UL>
00250     \warning <UL>
00251               <li>If the returned value is SCK_READ_OK, then the <i>buffer</i> points to a string of characters that has been allocated 
00252                   within the function. You must free it yourself (using free()).
00253               <li>This function assumes that the peer closes the connexion when the transfer is finished. This is the case for example if
00254                   you read the answer from a HTTP server (with no <i>keep alive</i>). 
00255              </UL>
00256 
00257  */
00258 
00259 int read_all_socket (int sockfd, char** buffer, long tt_sec, long tt_micro, unsigned long int* ellapsed_time, unsigned long int* size)
00260 {
00261   size_t                lu, total_lu, total_allocated;
00262   char                  buff[SCK_BUFFER_INIT_SIZE];
00263   int                   cr;
00264   unsigned long int     et;
00265 
00266 
00267   /* --------------------------------------------------------------------- */
00268   /* Initialize variables                                                  */
00269   /* --------------------------------------------------------------------- */
00270 
00271   et              = 0;
00272   lu              = 0;
00273   total_lu        = 0;
00274   *ellapsed_time  = 0;
00275   *size           = 0;
00276   total_allocated = SCK_BUFFER_INIT_SIZE;
00277   *buffer         = NULL;
00278 
00279   /* --------------------------------------------------------------------- */
00280   /* Allocate memory                                                       */
00281   /* --------------------------------------------------------------------- */
00282 
00283   *buffer = (char*)malloc(total_allocated);
00284   if (*buffer == NULL) { return SCK_READ_NO_MEM; }
00285 
00286   do {
00287         /* --------------------------------------------------------------- */
00288         /* Read data from socket                                           */
00289         /* --------------------------------------------------------------- */
00290 
00291         cr = read_from_socket (sockfd, buff, SCK_BUFFER_INIT_SIZE, &lu, tt_sec, tt_micro, &et);
00292 
00293         /* --------------------------------------------------------------- */
00294         /* Please remember that cr could be:                               */
00295         /*    -  SCK_READ_CHRONO_ERROR.                                    */
00296         /*    -  SCK_READ_TIMER_ERROR                                      */
00297         /*    -  SCK_READ_TIMEOUT                                          */
00298         /*    -  SCK_READ_READ_ERROR                                       */
00299         /*    -  SCK_READ_OK                                               */
00300         /*                                                                 */
00301         /* These values are shared between read_from_socket() and          */
00302         /* read_all_socket().                                              */
00303         /*                                                                 */
00304         /* read_all_socket() may also return SCK_READ_NO_MEM.              */
00305         /* --------------------------------------------------------------- */
00306 
00307         if (cr != SCK_READ_OK) { free (*buffer); return cr; }
00308 
00309         *ellapsed_time += et;
00310 
00311         /* --------------------------------------------------------------- */
00312         /* Increase buffer size if necessary and add data to the end of    */
00313         /* the buffer.                                                     */
00314         /* --------------------------------------------------------------- */
00315 
00316         while (total_lu+lu > total_allocated)
00317         {
00318           total_allocated *= 2;
00319           *buffer = (char*)realloc((void*)*buffer, total_allocated);
00320           if (*buffer == NULL)
00321           {
00322             free (*buffer);
00323             return SCK_READ_NO_MEM;
00324           }
00325         }
00326 
00327         memcpy ((void*)(*buffer + total_lu), (void*)buff, lu);
00328 
00329         total_lu += lu;
00330 
00331      } while (lu > 0);
00332 
00333   /* --------------------------------------------------------------------- */
00334   /* Add zero to terminate the string of chatacters                        */
00335   /* --------------------------------------------------------------------- */
00336 
00337 
00338   *size = total_lu;
00339 
00340   /* add the final zero */
00341   if (total_lu+1 > total_allocated)
00342   {
00343     total_allocated += 1;
00344     *buffer = (char*)realloc( (void*)*buffer, total_allocated);
00345     if (*buffer == NULL)
00346     {
00347        free (*buffer);
00348        return SCK_READ_NO_MEM;
00349     }
00350   }
00351 
00352   *(*buffer + total_lu) = 0;
00353 
00354   return SCK_READ_OK;
00355 }
00356 
00357 /*! \example test_sockets.c
00358     This file shows hos to use the function read_all_socket().
00359  */
00360 
00361 
00362 
00363 
00364 
00365 
00366 
00367 
00368 

Generated on Thu Apr 3 16:23:44 2003 for Common_C_libraries by doxygen1.3-rc1