tcp_socket.cpp
Go to the documentation of this file.
1 #include "tcp_socket.h"
2 
3 #include <argos3/core/utility/string_utilities.h>
4 
5 #include <arpa/inet.h>
6 #include <cstring>
7 #include <errno.h>
8 #include <netdb.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <poll.h>
12 #include <unistd.h>
13 #include <arpa/inet.h>
14 
15 namespace argos {
16 
17  /****************************************/
18  /****************************************/
19 
20  CTCPSocket::CTCPSocket(int n_stream) :
21  m_nStream(n_stream) {
22  }
23 
24  /****************************************/
25  /****************************************/
26 
28  m_nStream(c_other.m_nStream),
29  m_strAddress(c_other.m_strAddress) {
30  /* leave c_other in a default constructed state */
31  c_other.m_nStream = -1;
32  c_other.m_strAddress.clear();
33  }
34 
35  /****************************************/
36  /****************************************/
37 
39  /* disconnect the current socket */
40  Disconnect();
41  /* steal c_other's resources */
42  m_nStream = c_other.m_nStream;
43  m_strAddress = c_other.m_strAddress;
44  /* leave c_other in a default constructed state */
45  c_other.m_nStream = -1;
46  c_other.m_strAddress.clear();
47  /* return this instance */
48  return *this;
49  }
50 
51  /****************************************/
52  /****************************************/
53 
55  Disconnect();
56  }
57 
58  /****************************************/
59  /****************************************/
60 
61  void CTCPSocket::Connect(const std::string& str_hostname,
62  SInt32 n_port) {
63  /* Used to store the return value of the network function calls */
64  int nRetVal;
65  /* Get information on the available interfaces */
66  ::addrinfo tHints, *ptInterfaceInfo;
67  ::memset(&tHints, 0, sizeof(tHints));
68  tHints.ai_family = AF_INET; /* Only IPv4 is accepted */
69  tHints.ai_socktype = SOCK_STREAM; /* TCP socket */
70  nRetVal = ::getaddrinfo(str_hostname.c_str(),
71  ToString(n_port).c_str(),
72  &tHints,
73  &ptInterfaceInfo);
74  if(nRetVal != 0) {
75  THROW_ARGOSEXCEPTION("Error getting address information: " << ::gai_strerror(nRetVal));
76  }
77  /* Bind on the first interface available */
78  m_nStream = -1;
79  ::addrinfo* ptInterface = nullptr;
80  for(ptInterface = ptInterfaceInfo;
81  (ptInterface != nullptr) && (m_nStream == -1);
82  ptInterface = ptInterface->ai_next) {
83  m_nStream = ::socket(ptInterface->ai_family,
84  ptInterface->ai_socktype,
85  ptInterface->ai_protocol);
86  if(m_nStream > 0) {
87  if(::connect(m_nStream,
88  ptInterface->ai_addr,
89  ptInterface->ai_addrlen) == -1) {
90  m_nStream = -1;
91  THROW_ARGOSEXCEPTION("Can't connect to host: " << ::strerror(errno));
92  }
93  }
94  }
95  ::freeaddrinfo(ptInterfaceInfo);
96  }
97 
98  /****************************************/
99  /****************************************/
100 
102  SInt32 n_queue_length) {
103  /* Used to store the return value of the network function calls */
104  int nRetVal;
105  /* Get information on the available interfaces */
106  ::addrinfo tHints, *ptInterfaceInfo;
107  ::memset(&tHints, 0, sizeof(tHints));
108  tHints.ai_family = AF_INET; /* Only IPv4 is accepted */
109  tHints.ai_socktype = SOCK_STREAM; /* TCP socket */
110  tHints.ai_flags = AI_PASSIVE; /* Necessary for bind() later on */
111  nRetVal = ::getaddrinfo(nullptr,
112  ToString(n_port).c_str(),
113  &tHints,
114  &ptInterfaceInfo);
115  if(nRetVal != 0) {
116  THROW_ARGOSEXCEPTION("Error getting local address information: " << ::gai_strerror(nRetVal));
117  }
118  /* Bind on the first interface available */
119  m_nStream = -1;
120  ::addrinfo* ptInterface = nullptr;
121  for(ptInterface = ptInterfaceInfo;
122  (ptInterface != nullptr) && (m_nStream == -1);
123  ptInterface = ptInterface->ai_next) {
124  m_nStream = ::socket(ptInterface->ai_family,
125  ptInterface->ai_socktype,
126  ptInterface->ai_protocol);
127  if(m_nStream > 0) {
128  int nTrue = 1;
129  if(::setsockopt(m_nStream, SOL_SOCKET, SO_REUSEADDR, &nTrue, sizeof(nTrue)) != 0 ||
130  ::bind(m_nStream, ptInterface->ai_addr, ptInterface->ai_addrlen) != 0) {
131  Disconnect();
132  }
133  }
134  }
135  ::freeaddrinfo(ptInterfaceInfo);
136  if(m_nStream == -1) {
137  THROW_ARGOSEXCEPTION("Can't bind socket to any interface");
138  }
139  /* Listen on the socket */
140  if(::listen(m_nStream, n_queue_length) == -1) {
141  Disconnect();
142  THROW_ARGOSEXCEPTION("Can't listen on the socket" << ::strerror(errno));
143  }
144  }
145 
146  /****************************************/
147  /****************************************/
148 
149  void CTCPSocket::Accept(CTCPSocket& c_socket) {
150  /* Accept connections */
151  ::sockaddr tAddress;
152  ::socklen_t tAddressLen = sizeof(tAddress);
153  int nNewStream = ::accept(m_nStream, &tAddress, &tAddressLen);
154  if(nNewStream == -1) {
155  Disconnect();
156  THROW_ARGOSEXCEPTION("Error accepting connection: " << ::strerror(errno));
157  }
158  c_socket.m_nStream = nNewStream;
159  c_socket.m_strAddress = ::inet_ntoa(reinterpret_cast< ::sockaddr_in* >(&tAddress)->sin_addr);
160  }
161 
162  /****************************************/
163  /****************************************/
164 
166  ::close(m_nStream);
167  m_nStream = -1;
168  }
169 
170  /****************************************/
171  /****************************************/
172 
173  std::unordered_set<CTCPSocket::EEvent> CTCPSocket::GetEvents() {
174  std::unordered_set<EEvent> setEvents;
175  ::pollfd tFileDescriptor;
176  tFileDescriptor.fd = m_nStream;
177  tFileDescriptor.events = POLLIN | POLLOUT;
178  ::poll(&tFileDescriptor, 1, 1);
179  if(tFileDescriptor.revents & POLLIN)
180  setEvents.insert(EEvent::InputReady);
181  if(tFileDescriptor.revents & POLLOUT)
182  setEvents.insert(EEvent::OutputReady);
183  if(tFileDescriptor.revents & POLLHUP)
184  setEvents.insert(EEvent::HangUp);
185  if(tFileDescriptor.revents & POLLERR)
186  setEvents.insert(EEvent::ErrorCondition);
187  if(tFileDescriptor.revents & POLLNVAL)
188  setEvents.insert(EEvent::InvalidRequest);
189  return setEvents;
190  }
191 
192  /****************************************/
193  /****************************************/
194 
195  void CTCPSocket::SendBuffer(const UInt8* pun_buffer,
196  size_t un_size) {
197  ssize_t nSent;
198  while(un_size > 0) {
199  nSent = ::send(m_nStream, pun_buffer, un_size, 0);
200  if(nSent < 0) {
201  Disconnect();
202  THROW_ARGOSEXCEPTION("Error sending data: " << ::strerror(errno));
203  }
204  un_size -= nSent;
205  pun_buffer += nSent;
206  }
207  }
208 
209  /****************************************/
210  /****************************************/
211 
213  size_t un_size) {
214  ssize_t nReceived;
215  while(un_size > 0) {
216  nReceived = ::recv(m_nStream, pun_buffer, un_size, 0);
217  if(nReceived < 0){
218  Disconnect();
219  THROW_ARGOSEXCEPTION("Error receiving data: " << ::strerror(errno));
220  }
221  if(nReceived == 0) return false;
222  un_size -= nReceived;
223  pun_buffer += nReceived;
224  }
225  return true;
226  }
227 
228  /****************************************/
229  /****************************************/
230 
231  void CTCPSocket::SendByteArray(const CByteArray& c_byte_array) {
232  /* Send the length of the byte array */
233  UInt32 unSizeNBO = htonl(c_byte_array.Size());
234  SendBuffer(reinterpret_cast<UInt8*>(&unSizeNBO), sizeof(unSizeNBO));
235  /* Send the actual data */
236  SendBuffer(c_byte_array.ToCArray(), c_byte_array.Size());
237  }
238 
239  /****************************************/
240  /****************************************/
241 
243  /* Receive the length of the byte array */
244  UInt32 unSizeNBO;
245  if(ReceiveBuffer(reinterpret_cast<UInt8*>(&unSizeNBO), sizeof(unSizeNBO))) {
246  /* Receive the actual data */
247  c_byte_array.Resize(ntohl(unSizeNBO));
248  if(ReceiveBuffer(c_byte_array.ToCArray(), c_byte_array.Size())) {
249  return true;
250  }
251  }
252  return false;
253  }
254 
255  /****************************************/
256  /****************************************/
257 
258  void CTCPSocket::SendMsg(const CByteArray& c_payload, bool b_more) {
259  /* Make header */
260  UInt8 punHead[3];
261  *reinterpret_cast<UInt16*>(punHead) = htons(c_payload.Size());
262  punHead[2] = b_more;
263  /* Send header */
264  SendBuffer(punHead, 3);
265  /* Send payload */
266  SendBuffer(c_payload.ToCArray(), c_payload.Size());
267  }
268 
269  /****************************************/
270  /****************************************/
271 
272  void CTCPSocket::RecvMsg(CByteArray& c_payload) {
273  bool bMore = true;
274  UInt8 pchHead[3];
275  UInt16 unPayloadSize;
276  while(bMore) {
277  /* Receive header */
278  ReceiveBuffer(pchHead, 3);
279  unPayloadSize = ntohs(*reinterpret_cast<UInt16*>(pchHead));
280  bMore = (pchHead[2] == 1);
281  /* Receive the payload */
282  UInt8* pchBuf = new UInt8[unPayloadSize];
283  ReceiveBuffer(pchBuf, unPayloadSize);
284  /* Append payload to buffer */
285  c_payload.AddBuffer(pchBuf, unPayloadSize);
286  delete[] pchBuf;
287  }
288  }
289 
290  /****************************************/
291  /****************************************/
292 
293 }
#define THROW_ARGOSEXCEPTION(message)
This macro throws an ARGoS exception with the passed message.
signed int SInt32
32-bit signed integer.
Definition: datatypes.h:93
unsigned int UInt32
32-bit unsigned integer.
Definition: datatypes.h:97
unsigned char UInt8
8-bit unsigned integer.
Definition: datatypes.h:60
unsigned short UInt16
16-bit unsigned integer.
Definition: datatypes.h:78
The namespace containing all the ARGoS related code.
Definition: ci_actuator.h:12
std::string ToString(const T &t_value)
Converts the given parameter to a std::string.
Byte array utility class.
Definition: byte_array.h:28
size_t Size() const
Returns the current size of the byte array.
Definition: byte_array.h:66
CByteArray & AddBuffer(const UInt8 *pun_buffer, size_t un_size)
Appends bytes to the byte array.
Definition: byte_array.cpp:105
const UInt8 * ToCArray() const
Returns the contents of the byte array as a const c-style array.
Definition: byte_array.h:112
void Resize(size_t un_size, UInt8 un_value=0)
Resizes the byte array to the wanted size.
Definition: byte_array.h:83
void Accept(CTCPSocket &c_socket)
Accept a connection from a client.
Definition: tcp_socket.cpp:149
void SendByteArray(const CByteArray &c_byte_array)
Sends the passed byte array through the socket.
Definition: tcp_socket.cpp:231
CTCPSocket & operator=(const CTCPSocket &c_other)=delete
bool ReceiveByteArray(CByteArray &c_byte_array)
Receives the passed byte array through the socket.
Definition: tcp_socket.cpp:242
CTCPSocket(int n_stream=-1)
Definition: tcp_socket.cpp:20
void Disconnect()
Close the socket.
Definition: tcp_socket.cpp:165
void SendMsg(const CByteArray &c_payload, bool b_more=false)
Definition: tcp_socket.cpp:258
void RecvMsg(CByteArray &c_payload)
Definition: tcp_socket.cpp:272
void Listen(SInt32 n_port, SInt32 n_queue_length=10)
Listens for connections on the specified local port.
Definition: tcp_socket.cpp:101
void SendBuffer(const UInt8 *pun_buffer, size_t un_size)
Sends the passed buffer through the socket.
Definition: tcp_socket.cpp:195
std::unordered_set< EEvent > GetEvents()
Check the socket for events.
Definition: tcp_socket.cpp:173
bool ReceiveBuffer(UInt8 *pun_buffer, size_t un_size)
Fills the passed buffer with the data received through the socket.
Definition: tcp_socket.cpp:212
void Connect(const std::string &str_hostname, SInt32 n_port)
Connects this socket to the specified hostname and port.
Definition: tcp_socket.cpp:61