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>
41 #include "socket_client.hxx"
42 #include "t2n_exception.hxx"
50 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
51 socket_client_connection::socket_client_connection(int _port, const std::string& _server,
52 long long _connect_timeout_usec, int _max_retries,
53 std::ostream *_logstream, log_level_values _log_level)
54 : client_connection(), socket_handler(0,tcp_s)
56 max_retries=_max_retries;
57 connect_timeout_usec=_connect_timeout_usec;
62 set_logging(_logstream,_log_level);
66 tcp_connect(max_retries);
68 catch (t2n_communication_error &e)
70 lastErrorMsg=e.what();
71 LOGSTREAM(debug,"tcp connect error: " << lastErrorMsg);
72 // FIXME: Don't call virtual function in constructor. Currently not dangerous but bad design.
76 if (!connection::is_closed())
77 do_callbacks(new_connection);
80 /// returns a closed connection if connection could not be established, call get_last_error_msg() for details
81 socket_client_connection::socket_client_connection(const std::string& _path,
82 long long _connect_timeout_usec, int _max_retries,
83 std::ostream *_logstream, log_level_values _log_level)
84 : client_connection(), socket_handler(0,unix_s)
86 max_retries=_max_retries;
87 connect_timeout_usec=_connect_timeout_usec;
91 set_logging(_logstream,_log_level);
95 unix_connect(max_retries);
97 catch (t2n_communication_error &e)
99 lastErrorMsg=e.what();
100 LOGSTREAM(debug,"unix connect error: " << lastErrorMsg);
101 // FIXME: Don't call virtual function in constructor. Currently not dangerous
105 if (!connection::is_closed())
106 do_callbacks(new_connection);
110 * Destructor. Closes an open connection.
112 socket_client_connection::~socket_client_connection()
114 // Destructor of socket_handler will close the socket!
118 /// establish a connection via tcp
119 void socket_client_connection::tcp_connect(int max_retries)
121 struct sockaddr_in sock_addr;
123 sock_addr.sin_family = AF_INET;
124 sock_addr.sin_port = htons(port);
126 // find the target ip
127 if (inet_aton(server.c_str(),&sock_addr.sin_addr)==0)
129 struct hostent *server_hent;
130 server_hent=gethostbyname(server.c_str());
131 if (server_hent == NULL)
132 throw t2n_connect_error(string("can't find server ")+server);
134 memcpy(&sock_addr.sin_addr,server_hent->h_addr_list[0],sizeof(sock_addr.sin_addr));
137 sock = socket(PF_INET, SOCK_STREAM, 0);
139 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
143 connect_with_timeout((struct sockaddr *) &sock_addr,sizeof(sock_addr));
145 catch (t2n_connect_error &e)
147 // recurse if retries left
150 LOGSTREAM(debug,"retrying connect after connect error");
151 tcp_connect(max_retries-1);
154 throw t2n_connect_error("no more retries left after connect error");
158 /// establish a connection via unix-socket
159 void socket_client_connection::unix_connect(int max_retries)
161 struct sockaddr_un unix_addr;
163 unix_addr.sun_family = AF_UNIX;
164 strcpy (unix_addr.sun_path, path.c_str());
166 sock = socket(PF_UNIX, SOCK_STREAM, 0);
168 throw t2n_connect_error(string("socket() error: ")+strerror(errno));
172 connect_with_timeout((struct sockaddr *) &unix_addr, sizeof(unix_addr));
174 catch (t2n_connect_error &e)
176 // recurse if retries left
179 LOGSTREAM(debug,"retrying connect after connect error");
180 unix_connect(max_retries-1);
183 throw t2n_connect_error("no more retries left after connect error");
187 /// execute a connect on a prepared socket (tcp or unix) respecting timeouts
188 void socket_client_connection::connect_with_timeout(struct sockaddr *sock_addr,unsigned int sockaddr_size)
190 set_socket_options(sock);
192 /* non-blocking mode */
194 flflags=fcntl(sock,F_GETFL,0);
196 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
198 flflags &= (O_NONBLOCK ^ 0xFFFF);
199 if (fcntl(sock,F_SETFL,flflags) < 0)
200 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
203 LOGSTREAM(debug,"connect_with_timeout()");
204 int ret=::connect(sock,sock_addr, sockaddr_size);
208 if (errno==EINPROGRESS)
210 LOGSTREAM(debug,"connect_with_timeout(): EINPROGRESS");
214 struct timeval *timeout_ptr;
216 if (connect_timeout_usec == -1)
222 // convert timeout from long long usec to int sec + int usec
223 tval.tv_sec = connect_timeout_usec / 1000000;
224 tval.tv_usec = connect_timeout_usec % 1000000;
227 fd_set connect_socket_set;
228 FD_ZERO(&connect_socket_set);
229 FD_SET(sock,&connect_socket_set);
232 while ((ret=select(FD_SETSIZE, NULL, &connect_socket_set, NULL, timeout_ptr)) &&
233 ret < 0 && errno==EINTR);
236 throw t2n_connect_error(string("connect() error (select): ")+strerror(errno));
238 socklen_t sopt=sizeof(int);
240 ret=getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &sopt);
241 if (ret < 0 || valopt)
242 throw t2n_connect_error(string("connect() error (getsockopt): ")+strerror(errno));
245 throw t2n_connect_error(string("connect() error: ")+strerror(errno));
248 LOGSTREAM(debug,"connect_with_timeout(): success");
251 void socket_client_connection::close()
253 if (!client_connection::is_closed())
255 socket_handler::close();
256 client_connection::close();
260 /** @brief try to reconnect the current connection with the same connection credentials (host and port or path)
262 @note will throw an exeption if reconnecting not possible
264 void socket_client_connection::reconnect()
266 LOGSTREAM(debug,"reconnect()");
268 // close the current connection if still open
271 socket_type_value type=get_type();
274 tcp_connect(max_retries);
275 else if (type == unix_s)
276 unix_connect(max_retries);
278 // connection is open now, otherwise an execption would have been thrown
281 LOGSTREAM(debug,"reconnect() done, client_connection::is_closed() now " << client_connection::is_closed());