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>
43 #include "socket_handler.hxx"
44 #include "t2n_exception.hxx"
52 socket_handler::socket_handler(int _sock, socket_type_value _socket_type)
54 , recv_buffer_size( default_recv_buffer_size )
55 , write_block_size( default_write_block_size )
56 , write_timeout( default_write_timeout )
57 , socket_type(_socket_type)
62 * Destructor. Closes open socket
64 socket_handler::~socket_handler()
68 shutdown(sock,SHUT_RDWR);
75 /// close the underlying socket connection. Don't call directly, use the version provided
76 /// by the connection class you are using.
77 void socket_handler::close()
79 LOGSTREAM(debug,"close connection");
81 shutdown(sock,SHUT_RDWR);
87 /// set options like fast reuse and keepalive every socket should have
88 void socket_handler::set_socket_options(int sock)
92 /* fast reuse enable */
93 if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
94 EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
96 /* keepalive enable */
97 if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
98 EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
102 fdflags=fcntl(sock,F_GETFD, 0);
104 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
106 fdflags |= FD_CLOEXEC;
107 if (fcntl(sock,F_SETFD,fdflags) < 0)
108 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
110 /* non-blocking mode */
112 flflags=fcntl(sock,F_GETFL,0);
114 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
116 flflags |= O_NONBLOCK;
117 if (fcntl(sock,F_SETFL,flflags) < 0)
118 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
121 /// is the underlying socket connection still open?
122 bool socket_handler::is_closed()
124 int r=fcntl(sock,F_GETFL);
126 return !(r & O_ACCMODE);
131 * @brief set a new size for the receive buffer.
132 * @param new_recv_buffer_size the new size for the receive buffer.
134 * The receive buffer determines the amount of data which is tried to read at once
135 * from the underlying socket.
137 * The value is normalized to be at least 512 bytes and at max 32K bytes.
139 void socket_handler::set_recv_buffer_size(unsigned int new_recv_buffer_size)
141 recv_buffer_size= std::max( 512u, std::min( 32u * 1024u, new_recv_buffer_size ));
146 * @brief set new size for the data chunks when writeing.
147 * @param new_write_block_size the new chunk size.
149 * The write block size determines the amount of data which is tried to write
150 * to the socket when data needs to be sended.
151 * Since writing data is done in a loop, this does not limit the amunt of data which can
154 * The value is normalized to be at least 512 bytes and at max 32K bytes.
156 void socket_handler::set_write_block_size(unsigned int new_write_block_size)
158 write_block_size= std::max( 512u, std::min( 32u * 1024u, new_write_block_size ));
163 * @brief set new timeout for writing a block
164 * @param new_write_timeout the new timeout in usecs, -1: wait endless
166 * The write timeout determines the maximum amount of time that is waited
167 * between writing each block. If the timeout is exceeded, write will
168 * throw t2n_transfer_error
170 void socket_handler::set_write_timeout(long long new_write_timeout)
172 write_block_size=new_write_timeout;
176 /** @brief check if new data is waiting on the raw socket
177 @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
180 @param[out] usec_timeout_remaining microseconds from the timeout that were not used
182 bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout_remaining)
185 fd_set active_fd_set;
186 FD_ZERO (&active_fd_set);
187 FD_SET (sock, &active_fd_set);
191 struct timeval *timeout_ptr;
193 if (usec_timeout == -1)
199 // convert timeout from long long usec to int sec + int usec
200 tval.tv_sec = usec_timeout / 1000000;
201 tval.tv_usec = usec_timeout % 1000000;
204 int ret=select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr);
206 // return the timeout we did not use
207 // todo: this is linux specific according to man 2 select
208 if (usec_timeout > 0 && usec_timeout_remaining != NULL)
209 *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
217 /** @brief read data from the raw socket and copy it into the provided buffer
218 @param buffer the buffer where to append the new data
219 @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
222 @param[out] usec_timeout_remaining microseconds from the timeout that were not used
224 bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *usec_timeout_remaining)
226 // fast path for timeout==0
227 if (usec_timeout==0 || data_waiting(usec_timeout,usec_timeout_remaining))
228 return fill_buffer(buffer);
233 /** @brief read data from the raw socket and copy it into the provided buffer. Returns
234 instantly if no data is waiting.
235 @param buffer the buffer where to append the new data
237 bool socket_handler::fill_buffer(std::string& buffer)
239 bool try_again=false;
241 char socket_buffer[recv_buffer_size];
243 int nbytes = read (sock, socket_buffer, recv_buffer_size);
248 return false; // no data was waiting
249 else if (errno == EINTR)
251 // interrupted, try again
252 LOGSTREAM(debug,"EINTR received on read(), trying again");
256 EXCEPTIONSTREAM(error,t2n_transfer_error,"error reading from socket : " << strerror(errno));
260 if (nbytes == 0 && !try_again)
262 LOGSTREAM(debug,"0 bytes received on read(), closing connection");
267 // Data read -> store it
270 buffer.append(socket_buffer,nbytes);
271 LOGSTREAM(debug,nbytes << " bytes read");
274 // more data waiting -> recurse
284 /// writes raw data to the socket. Don't use directly, use the write() function provided by the
285 /// connection because it encapsulates the data.
286 void socket_handler::socket_write(const std::string& data)
288 unsigned int offset = 0;
289 while (offset < data.size())
291 unsigned int write_size=write_block_size;
293 if (offset+write_size > data.size())
294 write_size = data.size()-offset;
297 while ((rtn=::write(sock, data.data()+offset, write_size)) == -1 &&
298 (errno == EAGAIN || errno == EINTR))
300 wait_ready_to_write(sock,write_timeout);
301 LOGSTREAM(debug,"resuming write() call after EAGAIN or EINTR");
305 EXCEPTIONSTREAM(error,t2n_transfer_error,"write() returned " << strerror(errno));
306 else if (rtn != write_size)
308 LOGSTREAM(debug,"write() wrote " << rtn << " bytes, should have been "
309 << write_size << " (complete: " << data.size() << ")");
314 offset += write_size;
317 LOGSTREAM(debug,"wrote " << data.size() << " bytes");
320 } // eo socket_handler::socket_write(const std::string&)
322 /// wait until the socket is ready to write again
323 void socket_handler::wait_ready_to_write(int socket, long long write_block_timeout)
325 // prepare socket sets
327 fd_set except_set[1];
330 FD_SET(socket, write_set);
331 FD_SET(socket, except_set);
333 // prepare timeout struct
335 struct timeval *timeout_ptr;
337 if (write_block_timeout == -1)
343 // convert timeout from long long usec to int sec + int usec
344 tval.tv_sec = write_block_timeout / 1000000;
345 tval.tv_usec = write_block_timeout % 1000000;
348 // let's wait for the socket to become writable again...
350 while ((rtn=::select(socket+1, NULL, write_set, except_set, timeout_ptr)) ==-1 && errno == EINTR);
352 if (rtn > 0 && (!FD_ISSET(socket,write_set)) && FD_ISSET(socket, except_set))
354 // if we are selected but cannot write and have an exception
355 // we have serious trouble...
356 EXCEPTIONSTREAM(error,t2n_transfer_error,"exception on socket; cannot write any more.");
360 EXCEPTIONSTREAM(error,t2n_transfer_error,"timeout on select() for write");
363 EXCEPTIONSTREAM(error,t2n_transfer_error,"cannot select() for write: " << strerror(errno));