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