2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
27 #include <sys/types.h>
28 #include <sys/socket.h>
31 #include <arpa/inet.h>
32 #include <netinet/in.h>
42 #include "socket_client.hxx"
43 #include "t2n_exception.hxx"
51 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
52 socket_client_connection::socket_client_connection(int _port, const std::string& _server,
53 long long _connect_timeout_usec, int _max_retries,
54 std::ostream *_logstream, log_level_values _log_level)
55 : client_connection(), socket_handler(0,tcp_s)
57 max_retries=_max_retries;
58 connect_timeout_usec=_connect_timeout_usec;
63 set_logging(_logstream,_log_level);
67 tcp_connect(max_retries);
69 catch (t2n_communication_error &e)
71 lastErrorMsg=e.what();
72 LOGSTREAM(debug,"tcp connect error: " << lastErrorMsg);
73 // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
77 if (!connection::is_closed())
78 do_callbacks(new_connection);
81 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
82 socket_client_connection::socket_client_connection(const std::string& _path,
83 long long _connect_timeout_usec, int _max_retries,
84 std::ostream *_logstream, log_level_values _log_level)
85 : client_connection(), socket_handler(0,unix_s)
87 max_retries=_max_retries;
88 connect_timeout_usec=_connect_timeout_usec;
92 set_logging(_logstream,_log_level);
96 unix_connect(max_retries);
98 catch (t2n_communication_error &e)
100 lastErrorMsg=e.what();
101 LOGSTREAM(debug,"unix connect error: " << lastErrorMsg);
102 // FIXME: Don't call virtual function in constructor. Currently not dangerous
106 if (!connection::is_closed())
107 do_callbacks(new_connection);
111 * Destructor. Closes an open connection.
113 socket_client_connection::~socket_client_connection()
115 // Destructor of socket_handler will close the socket!
119 /// establish a connection via tcp
120 void socket_client_connection::tcp_connect(int max_retries)
122 struct sockaddr_in sock_addr;
124 sock_addr.sin_family = AF_INET;
125 sock_addr.sin_port = htons(port);
127 // find the target ip
128 if (inet_aton(server.c_str(),&sock_addr.sin_addr)==0)
130 struct hostent *server_hent;
131 server_hent=gethostbyname(server.c_str());
132 if (server_hent == NULL)
133 throw t2n_connect_error(string("can't find server ")+server);
135 memcpy(&sock_addr.sin_addr,server_hent->h_addr_list[0],sizeof(sock_addr.sin_addr));
138 sock = socket(PF_INET, SOCK_STREAM, 0);
140 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
144 connect_with_timeout((struct sockaddr *) &sock_addr,sizeof(sock_addr));
146 catch (t2n_connect_error &e)
148 // recurse if retries left
151 LOGSTREAM(debug,"retrying connect after connect error");
152 tcp_connect(max_retries-1);
155 throw t2n_connect_error("no more retries left after connect error");
159 /// establish a connection via unix-socket
160 void socket_client_connection::unix_connect(int max_retries)
162 struct sockaddr_un unix_addr;
163 size_t path_size = path.size();
165 unix_addr.sun_family = AF_UNIX;
167 if (path_size >= sizeof(unix_addr.sun_path))
169 throw t2n_connect_error((std::string)"path '"
171 + "' exceeds permissible UNIX socket path length");
174 memcpy(unix_addr.sun_path, path.c_str(), path_size);
175 unix_addr.sun_path[path_size] = '\0';
177 sock = socket(PF_UNIX, SOCK_STREAM, 0);
179 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
183 connect_with_timeout((struct sockaddr *) &unix_addr, sizeof(unix_addr));
185 catch (t2n_connect_error &e)
187 // recurse if retries left
190 LOGSTREAM(debug,"retrying connect after connect error");
191 unix_connect(max_retries-1);
194 throw t2n_connect_error("no more retries left after connect error");
198 /// execute a connect on a prepared socket (tcp or unix) respecting timeouts
199 void socket_client_connection::connect_with_timeout(struct sockaddr *sock_addr,unsigned int sockaddr_size)
201 set_socket_options(sock);
203 /* non-blocking mode */
205 flflags=fcntl(sock,F_GETFL,0);
207 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
209 flflags &= (O_NONBLOCK ^ 0xFFFF);
210 if (fcntl(sock,F_SETFL,flflags) < 0)
211 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
214 LOGSTREAM(debug,"connect_with_timeout()");
215 int ret=::connect(sock,sock_addr, sockaddr_size);
219 if (errno==EINPROGRESS)
221 LOGSTREAM(debug,"connect_with_timeout(): EINPROGRESS");
225 struct timeval *timeout_ptr;
227 if (connect_timeout_usec == -1)
233 // convert timeout from long long usec to int sec + int usec
234 tval.tv_sec = connect_timeout_usec / 1000000;
235 tval.tv_usec = connect_timeout_usec % 1000000;
238 fd_set connect_socket_set;
239 FD_ZERO(&connect_socket_set);
240 FD_SET(sock,&connect_socket_set);
243 while ((ret=select(FD_SETSIZE, NULL, &connect_socket_set, NULL, timeout_ptr)) &&
244 ret < 0 && errno==EINTR);
247 throw t2n_connect_error(string("connect() error (select): ")+strerror(errno));
249 socklen_t sopt=sizeof(int);
251 ret=getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &sopt);
252 if (ret < 0 || valopt)
253 throw t2n_connect_error(string("connect() error (getsockopt): ")+strerror(errno));
256 throw t2n_connect_error(string("connect() error: ")+strerror(errno));
259 LOGSTREAM(debug,"connect_with_timeout(): success");
262 void socket_client_connection::close()
264 if (!client_connection::is_closed())
266 socket_handler::close();
267 client_connection::close();
271 /** @brief try to reconnect the current connection with the same connection credentials (host and port or path)
273 @note will throw an exeption if reconnecting not possible
275 void socket_client_connection::reconnect()
277 LOGSTREAM(debug,"reconnect()");
279 // close the current connection if still open
282 socket_type_value type=get_type();
285 tcp_connect(max_retries);
286 else if (type == unix_s)
287 unix_connect(max_retries);
289 // connection is open now, otherwise an execption would have been thrown
292 LOGSTREAM(debug,"reconnect() done, client_connection::is_closed() now " << client_connection::is_closed());