IBR-DTNSuite  0.8
ibrcommon/ibrcommon/net/tcpclient.cpp
Go to the documentation of this file.
00001 /*
00002  * tcpclient.cpp
00003  *
00004  *  Created on: 29.07.2009
00005  *      Author: morgenro
00006  */
00007 
00008 #include "ibrcommon/config.h"
00009 #include "ibrcommon/net/tcpclient.h"
00010 #include <sys/types.h>
00011 #include <sys/socket.h>
00012 #include <sys/un.h>
00013 #include <netdb.h>
00014 #include <unistd.h>
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 
00019 #include <streambuf>
00020 #include <netinet/in.h>
00021 #include <arpa/inet.h>
00022 #include <sstream>
00023 
00024 namespace ibrcommon
00025 {
00026         tcpclient::tcpclient()
00027         {
00028         }
00029 
00030         tcpclient::tcpclient(const ibrcommon::File &s)
00031         {
00032                 open(s);
00033         }
00034 
00035         void tcpclient::open(const ibrcommon::File &s)
00036         {
00037                 int len = 0;
00038                 struct sockaddr_un saun;
00039 
00040                 /*
00041                  * Get a socket to work with.  This socket will
00042                  * be in the UNIX domain, and will be a
00043                  * stream socket.
00044                  */
00045                 if ((_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
00046                         throw SocketException("Could not create a socket.");
00047                 }
00048 
00049                 /*
00050                  * Create the address we will be connecting to.
00051                  */
00052                 saun.sun_family = AF_UNIX;
00053                 strcpy(saun.sun_path, s.getPath().c_str());
00054 
00055                 /*
00056                  * Try to connect to the address.  For this to
00057                  * succeed, the server must already have bound
00058                  * this address, and must have issued a listen()
00059                  * request.
00060                  *
00061                  * The third argument indicates the "length" of
00062                  * the structure, not just the length of the
00063                  * socket name.
00064                  */
00065                 len = sizeof(saun.sun_family) + strlen(saun.sun_path);
00066 
00067                 if (connect(_socket, (struct sockaddr *)&saun, len) < 0) {
00068                         throw SocketException("Could not connect to the named socket.");
00069                 }
00070         }
00071 
00072         tcpclient::tcpclient(const string &address, const int port, size_t timeout)
00073         {
00074                 open(address, port, timeout);
00075         }
00076 
00077         void tcpclient::open(const string &address, const int port, size_t timeout)
00078         {
00079                 struct addrinfo hints;
00080                 struct addrinfo *walk;
00081                 memset(&hints, 0, sizeof(struct addrinfo));
00082                 hints.ai_family = PF_UNSPEC;
00083                 hints.ai_socktype = SOCK_STREAM;
00084 
00085                 struct addrinfo *res;
00086                 int ret;
00087 
00088                 std::stringstream ss; ss << port; std::string port_string = ss.str();
00089 
00090                 if ((ret = getaddrinfo(address.c_str(), port_string.c_str(), &hints, &res)) != 0)
00091                 {
00092                         throw SocketException("getaddrinfo(): " + std::string(gai_strerror(ret)));
00093                 }
00094 
00095                 if (res == NULL)
00096                 {
00097                         throw SocketException("Could not connect to the server.");
00098                 }
00099 
00100                 try {
00101                         for (walk = res; walk != NULL; walk = walk->ai_next) {
00102                                 _socket = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol);
00103                                 if (_socket < 0){
00104                                         /* Hier kann eine Fehlermeldung hin, z.B. mit warn() */
00105 
00106                                         if (walk->ai_next ==  NULL)
00107                                         {
00108                                                 throw SocketException("Could not create a socket.");
00109                                         }
00110                                         continue;
00111                                 }
00112 
00113                                 if (timeout == 0)
00114                                 {
00115                                         if (connect(_socket, walk->ai_addr, walk->ai_addrlen) != 0) {
00116                                                 ::close(_socket);
00117                                                 _socket = -1;
00118                                                 /* Hier kann eine Fehlermeldung hin, z.B. mit warn() */
00119                                                 if (walk->ai_next ==  NULL)
00120                                                 {
00121                                                         throw SocketException("Could not connect to the server.");
00122                                                 }
00123                                                 continue;
00124                                         }
00125                                 }
00126                                 else
00127                                 {
00128                                         // timeout is requested, set socket to non-blocking
00129                                         vsocket::set_non_blocking(_socket);
00130 
00131                                         // now connect to the host (this returns immediately
00132                                         ::connect(_socket, walk->ai_addr, walk->ai_addrlen);
00133 
00134                                         try {
00135                                                 bool read = false;
00136                                                 bool write = true;
00137                                                 bool error = false;
00138 
00139                                                 // now wait until we could write on this socket
00140                                                 tcpstream::select(_interrupt_pipe_read[1], read, write, error, timeout);
00141 
00142                                                 // set the socket to blocking again
00143                                                 vsocket::set_non_blocking(_socket, false);
00144 
00145                                                 // check if the attempt was successful
00146                                                 int err = 0;
00147                                                 socklen_t err_len = sizeof(err_len);
00148                                                 ::getsockopt(_socket, SOL_SOCKET, SO_ERROR, &err, &err_len);
00149 
00150                                                 if (err != 0)
00151                                                 {
00152                                                         throw SocketException("Could not connect to the server.");
00153                                                 }
00154                                         } catch (const select_exception &ex) {
00155                                                 throw SocketException("Could not connect to the server.");
00156                                         }
00157                                 }
00158                                 break;
00159                         }
00160 
00161                         freeaddrinfo(res);
00162                 } catch (ibrcommon::Exception&) {
00163                         freeaddrinfo(res);
00164                         throw;
00165                 }
00166         }
00167 
00168         tcpclient::~tcpclient()
00169         {
00170         }
00171 }