IBR-DTNSuite  0.10
socketstream.cpp
Go to the documentation of this file.
1 /*
2  * socketstream.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
23 #include "ibrcommon/Logger.h"
24 #include <string.h>
25 
26 namespace ibrcommon
27 {
28  socketstream::socketstream(clientsocket *sock, size_t buffer_size)
29  : std::iostream(this), errmsg(ERROR_NONE), _bufsize(buffer_size), in_buf_(_bufsize), out_buf_(_bufsize)
30  {
31  // clear the local timer
32  timerclear(&_timeout);
33 
34  // mark the buffer as free
35  setp(&out_buf_[0], &out_buf_[0] + _bufsize - 1);
36  setg(0, 0, 0);
37 
38  _socket.add(sock);
39  _socket.up();
40  }
41 
43  {
44  _socket.destroy();
45  }
46 
48  {
49  this->flush();
50  _socket.down();
51  }
52 
53  void socketstream::setTimeout(const timeval &val)
54  {
55  ::memcpy(&_timeout, &val, sizeof _timeout);
56  }
57 
59  {
60  int ret = std::char_traits<char>::eq_int_type(this->overflow(
61  std::char_traits<char>::eof()), std::char_traits<char>::eof()) ? -1
62  : 0;
63 
64  return ret;
65  }
66 
67  std::char_traits<char>::int_type socketstream::overflow(std::char_traits<char>::int_type c)
68  {
69  char *ibegin = &out_buf_[0];
70  char *iend = pptr();
71 
72  // mark the buffer as free
73  setp(&out_buf_[0], &out_buf_[0] + _bufsize - 1);
74 
75  if (!std::char_traits<char>::eq_int_type(c, std::char_traits<char>::eof()))
76  {
77  *iend++ = std::char_traits<char>::to_char_type(c);
78  }
79 
80  // if there is nothing to send, just return
81  if ((iend - ibegin) == 0)
82  {
83  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 80) << "overflow() nothing to sent" << IBRCOMMON_LOGGER_ENDL;
84  return std::char_traits<char>::not_eof(c);
85  }
86 
87  try {
88  socketset writeset;
89  _socket.select(NULL, &writeset, NULL, NULL);
90 
91  // error checking
92  if (writeset.size() == 0) {
93  throw socket_exception("no select result returned");
94  }
95 
96  // bytes to send
97  ssize_t bytes = (iend - ibegin);
98 
99  // send the data
100  clientsocket &sock = static_cast<clientsocket&>(**(writeset.begin()));
101 
102  ssize_t ret = sock.send(&out_buf_[0], (iend - ibegin), 0);
103 
104  // check how many bytes are sent
105  if (ret < bytes)
106  {
107  // we did not sent all bytes
108  char *resched_begin = ibegin + ret;
109  char *resched_end = iend;
110 
111  // bytes left to send
112  size_t bytes_left = resched_end - resched_begin;
113 
114  // move the data to the begin of the buffer
115  ::memcpy(ibegin, resched_begin, bytes_left);
116 
117  // new free buffer
118  char *buffer_begin = ibegin + bytes_left;
119 
120  // mark the buffer as free
121  setp(buffer_begin, &out_buf_[0] + _bufsize - 1);
122  }
123  } catch (const vsocket_interrupt &e) {
125  close();
126  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 85) << "select interrupted: " << e.what() << IBRCOMMON_LOGGER_ENDL;
127  throw;
128  } catch (const socket_error &err) {
129  if (err.code() == ERROR_AGAIN) {
130  return overflow(c);
131  }
132 
133  // set the last error code
134  errmsg = err.code();
135 
136  // close the stream/socket due to failures
137  close();
138 
139  // create a detailed exception message
140  std::stringstream ss; ss << "send() failed: " << err.code();
141  throw stream_exception(ss.str());
142  } catch (const socket_exception &ex) {
143  // set the last error code
145 
146  // close the stream/socket due to failures
147  close();
148 
149  // create a detailed exception message
150  throw stream_exception("<tcpstream> send() timed out");
151  }
152 
153  return std::char_traits<char>::not_eof(c);
154  }
155 
156  std::char_traits<char>::int_type socketstream::underflow()
157  {
158  try {
159  socketset readset;
160 
161  if (timerisset(&_timeout)) {
162  timeval to_copy;
163  ::memcpy(&to_copy, &_timeout, sizeof to_copy);
164  _socket.select(&readset, NULL, NULL, &to_copy);
165  } else {
166  _socket.select(&readset, NULL, NULL, NULL);
167  }
168 
169  // error checking
170  if (readset.size() == 0) {
171  throw socket_exception("no select result returned");
172  }
173 
174  // send the data
175  clientsocket &sock = static_cast<clientsocket&>(**(readset.begin()));
176 
177  // read some bytes
178  ssize_t bytes = sock.recv(&in_buf_[0], _bufsize, 0);
179 
180  // end of stream
181  if (bytes == 0)
182  {
184  close();
185  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 85) << "recv() returned zero: " << errno << IBRCOMMON_LOGGER_ENDL;
186  return std::char_traits<char>::eof();
187  }
188 
189  // Since the input buffer content is now valid (or is new)
190  // the get pointer should be initialized (or reset).
191  setg(&in_buf_[0], &in_buf_[0], &in_buf_[0] + bytes);
192 
193  return std::char_traits<char>::not_eof(in_buf_[0]);
194  } catch (const vsocket_interrupt &e) {
196  close();
197  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 85) << "select interrupted: " << e.what() << IBRCOMMON_LOGGER_ENDL;
198  return std::char_traits<char>::eof();
199  } catch (const socket_error &err) {
200  // set the last error code
201  errmsg = err.code();
202 
203  // close the stream/socket due to failures
204  close();
205 
206  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 75) << "recv() failed: " << err.code() << IBRCOMMON_LOGGER_ENDL;
207  } catch (const socket_exception &ex) {
208  IBRCOMMON_LOGGER_DEBUG_TAG("socketstream", 75) << "recv() failed: " << ex.what() << IBRCOMMON_LOGGER_ENDL;
209  }
210 
211  return std::char_traits<char>::eof();
212  }
213 } /* namespace ibrcommon */