uclient.cc

00001 
00024 #include <cstdlib>
00025 #include "libport/cstdio"
00026 #include <cerrno>
00027 
00028 #include <locale.h>
00029 #include "urbi/uclient.hh"
00030 #include "libport/lockable.hh"
00031 #include "libport/thread.hh"
00032 
00033 // In the long run, this should be part of libport, but I don't know
00034 // where actually :( Should it be libport/sys/types.hh?  Or unistd.hh?
00035 // What standard header should do that?
00036 
00037 #ifdef WIN32
00038 // On windows, file descriptors are defined as u_int (i.e., unsigned int).
00039 # define LIBPORT_FD_SET(N, P) FD_SET(static_cast<u_int>(N), P)
00040 #else
00041 # define LIBPORT_FD_SET(N, P) FD_SET(N, P)
00042 #endif
00043 
00044 #ifdef WIN32
00045 # include "libport/windows.hh"
00046 # include <fcntl.h>
00047 # include <io.h>
00048 # include <winsock.h>
00049 #else
00050 # include <sys/time.h>
00051 # include <sys/socket.h>
00052 # include <netinet/in.h>
00053 # include <arpa/inet.h>
00054 # include <netdb.h>
00055 # include <unistd.h>
00056 #endif
00057 
00058 namespace urbi
00059 {
00064   UClient::UClient(const char *_host, int _port, int _buflen)
00065     : UAbstractClient(_host, _port, _buflen)
00066   {
00067     setlocale(LC_NUMERIC, "C");
00068     control_fd[0] = control_fd[1] = -1;
00069 
00070 #ifndef WIN32
00071     if (::pipe(control_fd) == -1)
00072     {
00073       rc = -1;
00074       perror("UClient::UClient failed to create pipe");
00075       return;
00076     }
00077 #endif
00078 
00079     // Address resolution stage.
00080     struct hostent *hen;                // host-to-IP translation
00081     struct sockaddr_in sa;      // Internet address struct
00082 
00083     memset(&sa, 0, sizeof (sa));
00084 #ifdef WIN32
00085     WSADATA wsaData;
00086     WORD wVersionRequested;
00087     wVersionRequested = MAKEWORD( 1, 1 );
00088     WSAStartup( wVersionRequested, &wsaData );
00089 #endif
00090     sa.sin_family = AF_INET;
00091     sa.sin_port = htons(port);
00092 
00093     hen = gethostbyname(host);
00094 
00095     if (!hen)
00096     {
00097       // maybe it is an IP address
00098       sa.sin_addr.s_addr = inet_addr(host);
00099       if (sa.sin_addr.s_addr == INADDR_NONE)
00100       {
00101         printf("UClient::UClient cannot resolve host name.\n");
00102         rc = -1;
00103         return;
00104       }
00105     }
00106     else
00107       memcpy(&sa.sin_addr.s_addr, hen->h_addr_list[0], hen->h_length);
00108 
00109     sd = socket(AF_INET, SOCK_STREAM, 0);
00110     if (sd < 0)
00111     {
00112       printf("UClient::UClient socket allocation failed.\n");
00113       rc = -1;
00114       return;
00115     }
00116 
00117     // now connect to the remote server.
00118     rc = connect(sd, (struct sockaddr *) &sa, sizeof (sa));
00119 
00120     // If we attempt to connect too fast to aperios ipstack it will fail.
00121     if (rc)
00122     {
00123       usleep(20000);
00124       rc = connect(sd, (struct sockaddr *) &sa, sizeof (sa));
00125     }
00126 
00127     // Check there was no error.
00128     if (rc)
00129     {
00130       std::cerr << "UClient::UClient cannot connect." << std::endl;
00131       return;
00132     }
00133 
00134     //check that it really worked
00135     int pos=0;
00136     while (pos==0)
00137       pos = ::recv(sd, recvBuffer, buflen, 0);
00138     if (pos<0)
00139     {
00140       rc = pos;
00141       printf("UClient::UClient cannot connect: read error %d.\n", rc);
00142       return;
00143     }
00144     else
00145       recvBufferPosition = pos;
00146     recvBuffer[recvBufferPosition] = 0;
00147     thread = libport::startThread(this, &UClient::listenThread);
00148     if (!defaultClient)
00149       defaultClient = this;
00150   }
00151 
00152   UClient::~UClient()
00153   {
00154     close(sd);
00155     sd = -1;
00156     if (control_fd[1] != -1 )
00157       ::write(control_fd[1], "a", 1);
00158     //must wait for listen thread to terminate
00159     libport::joinThread(thread);
00160     if (control_fd[1] != -1)
00161       close(control_fd[1]);
00162     if (control_fd[0] != -1)
00163       close(control_fd[0]);
00164   }
00165 
00166 
00167   bool
00168   UClient::canSend(int)
00169   {
00170     return true;
00171   }
00172 
00173 
00174   int
00175   UClient::effectiveSend(const void  * buffer, int size)
00176   {
00177 #if DEBUG
00178     char output[size+1];
00179     memcpy (static_cast<void*> (output), buffer, size);
00180     output[size]=0;
00181     cout << ">>>> SENT : [" << output << "]" << endl;
00182 #endif
00183     if (rc)
00184       return -1;
00185     int pos = 0;
00186     while (pos!=size)
00187     {
00188       int retval = ::send(sd, (char *) buffer + pos, size-pos, 0);
00189       if (retval<0)
00190       {
00191         rc = retval;
00192         return rc;
00193       }
00194       pos += retval;
00195     }
00196     return 0;
00197   }
00198 
00199   void
00200   UClient::listenThread()
00201   {
00202     fd_set rfds;
00203     int maxfd=1+ (sd>control_fd[0]? sd:control_fd[0]);
00204     int res;
00205     while (true)
00206     {
00207       do {
00208         if (sd==-1)
00209           return;
00210         FD_ZERO(&rfds);
00211         LIBPORT_FD_SET(sd, &rfds);
00212 #ifndef WIN32
00213         LIBPORT_FD_SET(control_fd[0], &rfds);
00214 #endif
00215         struct timeval tme;
00216         tme.tv_sec = 1;
00217         tme.tv_usec = 0;
00218         res = select(maxfd+1, &rfds, NULL, NULL, &tme);
00219         if (res < 0 && errno != EINTR)
00220         {
00221           this->rc = -1;
00222 #ifdef WIN32
00223           res = WSAGetLastError();
00224 #endif
00225           std::cerr << "select error "<<res<<std::endl;
00226           //TODO when error will be implemented, send an error msg
00227           //TODO maybe try to reconnect?
00228           return;
00229         }
00230         if (res == -1)
00231         {
00232           res=0;
00233           continue;
00234         }
00235 #ifndef WIN32
00236         if (res != 0 && FD_ISSET(control_fd[0], &rfds))
00237           return;
00238 #endif
00239       } while (res == 0);
00240       int count = ::recv(sd,
00241                          &recvBuffer[recvBufferPosition],
00242                          buflen - recvBufferPosition - 1, 0);
00243       if (count < 0)
00244       {
00245         rc = -1;
00246         std::cerr << "error " << count << std::endl;
00247         //TODO when error will be implemented, send an error msg
00248         //TODO maybe try to reconnect?
00249         return;
00250       }
00251 
00252       recvBufferPosition += count;
00253       recvBuffer[recvBufferPosition] = 0;
00254       processRecvBuffer();
00255     }
00256   }
00257 
00258 
00259   void
00260   UClient::printf(const char * format, ...)
00261   {
00262     va_list arg;
00263     va_start(arg, format);
00264     vfprintf(stderr, format, arg);
00265     va_end(arg);
00266   }
00267 
00268   unsigned int UClient::getCurrentTime() const
00269   {
00270   // FIXME: Put this into libport.
00271 #ifdef WIN32
00272     return GetTickCount();
00273 #else
00274     struct timeval tv;
00275     gettimeofday(&tv, NULL);
00276     return tv.tv_sec*1000+tv.tv_usec/1000;
00277 #endif
00278   }
00279 
00280   void execute(void)
00281   {
00282 #ifdef WIN32
00283     while (true)
00284       Sleep(100000);
00285 #else
00286     while (true)
00287       sleep(100);
00288 #endif
00289   }
00290 
00291   void exit(int code)
00292   {
00293     ::exit(code);
00294   }
00295 
00296 
00297   UClient & connect(const char* host)
00298   {
00299     return *new UClient(host);
00300   }
00301 
00302 } // namespace urbi

Generated on Tue Apr 10 17:45:44 2007 for URBISDK by  doxygen 1.5.1