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 <unistd.h>
12 
13 namespace argos {
14 
15  /****************************************/
16  /****************************************/
17 
18  CTCPSocket::CTCPSocket(int n_stream) :
19  m_nStream(n_stream) {
20  }
21 
22  /****************************************/
23  /****************************************/
24 
26  Disconnect();
27  }
28 
29  /****************************************/
30  /****************************************/
31 
32  void CTCPSocket::Connect(const std::string& str_hostname,
33  SInt32 n_port) {
34  /* Used to store the return value of the network function calls */
35  int nRetVal;
36  /* Get information on the available interfaces */
37  ::addrinfo tHints, *ptInterfaceInfo;
38  ::memset(&tHints, 0, sizeof(tHints));
39  tHints.ai_family = AF_INET; /* Only IPv4 is accepted */
40  tHints.ai_socktype = SOCK_STREAM; /* TCP socket */
41  nRetVal = ::getaddrinfo(str_hostname.c_str(),
42  ToString(n_port).c_str(),
43  &tHints,
44  &ptInterfaceInfo);
45  if(nRetVal != 0) {
46  THROW_ARGOSEXCEPTION("Error getting address information: " << ::gai_strerror(nRetVal));
47  }
48  /* Bind on the first interface available */
49  m_nStream = -1;
50  ::addrinfo* ptInterface = NULL;
51  for(ptInterface = ptInterfaceInfo;
52  (ptInterface != NULL) && (m_nStream == -1);
53  ptInterface = ptInterface->ai_next) {
54  m_nStream = ::socket(ptInterface->ai_family,
55  ptInterface->ai_socktype,
56  ptInterface->ai_protocol);
57  if(m_nStream > 0) {
58  if(::connect(m_nStream,
59  ptInterface->ai_addr,
60  ptInterface->ai_addrlen) == -1) {
61  m_nStream = -1;
62  THROW_ARGOSEXCEPTION("Can't connect to host: " << ::strerror(errno));
63  }
64  }
65  }
66  ::freeaddrinfo(ptInterfaceInfo);
67  }
68 
69  /****************************************/
70  /****************************************/
71 
73  SInt32 n_queue_length) {
74  /* Used to store the return value of the network function calls */
75  int nRetVal;
76  /* Get information on the available interfaces */
77  ::addrinfo tHints, *ptInterfaceInfo;
78  ::memset(&tHints, 0, sizeof(tHints));
79  tHints.ai_family = AF_INET; /* Only IPv4 is accepted */
80  tHints.ai_socktype = SOCK_STREAM; /* TCP socket */
81  tHints.ai_flags = AI_PASSIVE; /* Necessary for bind() later on */
82  nRetVal = ::getaddrinfo(NULL,
83  ToString(n_port).c_str(),
84  &tHints,
85  &ptInterfaceInfo);
86  if(nRetVal != 0) {
87  THROW_ARGOSEXCEPTION("Error getting local address information: " << ::gai_strerror(nRetVal));
88  }
89  /* Bind on the first interface available */
90  m_nStream = -1;
91  ::addrinfo* ptInterface = NULL;
92  for(ptInterface = ptInterfaceInfo;
93  (ptInterface != NULL) && (m_nStream == -1);
94  ptInterface = ptInterface->ai_next) {
95  m_nStream = ::socket(ptInterface->ai_family,
96  ptInterface->ai_socktype,
97  ptInterface->ai_protocol);
98  if(m_nStream > 0) {
99  int nTrue = 1;
100  if((::setsockopt(m_nStream,
101  SOL_SOCKET,
102  SO_REUSEADDR,
103  &nTrue,
104  sizeof(nTrue)) != -1)
105  &&
106  (::bind(m_nStream,
107  ptInterface->ai_addr,
108  ptInterface->ai_addrlen) == -1)) {
109  Disconnect();
110  }
111  }
112  }
113  ::freeaddrinfo(ptInterfaceInfo);
114  if(m_nStream == -1) {
115  THROW_ARGOSEXCEPTION("Can't bind socket to any interface");
116  }
117  /* Listen on the socket */
118  if(::listen(m_nStream, n_queue_length) == -1) {
119  Disconnect();
120  THROW_ARGOSEXCEPTION("Can't listen on the socket" << ::strerror(errno));
121  }
122  }
123 
124  /****************************************/
125  /****************************************/
126 
127  void CTCPSocket::Accept(CTCPSocket& c_socket) {
128  /* Accept connections */
129  ::sockaddr tAddress;
130  ::socklen_t tAddressLen = sizeof(tAddress);
131  int nNewStream = ::accept(m_nStream, &tAddress, &tAddressLen);
132  if(nNewStream == -1) {
133  Disconnect();
134  THROW_ARGOSEXCEPTION("Error accepting connection: " << ::strerror(errno));
135  }
136  c_socket.m_nStream = nNewStream;
137  c_socket.m_strAddress = ::inet_ntoa(reinterpret_cast< ::sockaddr_in* >(&tAddress)->sin_addr);
138  }
139 
140  /****************************************/
141  /****************************************/
142 
144  ::close(m_nStream);
145  m_nStream = -1;
146  m_strAddress = "";
147  }
148 
149  /****************************************/
150  /****************************************/
151 
152  void CTCPSocket::SendBuffer(const UInt8* pun_buffer,
153  size_t un_size) {
154  ssize_t nSent;
155  while(un_size > 0) {
156  nSent = ::send(m_nStream, pun_buffer, un_size, 0);
157  if(nSent < 0) {
158  Disconnect();
159  THROW_ARGOSEXCEPTION("Error sending data: " << ::strerror(errno));
160  }
161  un_size -= nSent;
162  pun_buffer += nSent;
163  }
164  }
165 
166  /****************************************/
167  /****************************************/
168 
170  size_t un_size) {
171  ssize_t nReceived;
172  while(un_size > 0) {
173  nReceived = ::recv(m_nStream, pun_buffer, un_size, 0);
174  if(nReceived < 0){
175  Disconnect();
176  THROW_ARGOSEXCEPTION("Error receiving data: " << ::strerror(errno));
177  }
178  if(nReceived == 0) return false;
179  un_size -= nReceived;
180  pun_buffer += nReceived;
181  }
182  return true;
183  }
184 
185  /****************************************/
186  /****************************************/
187 
188  void CTCPSocket::SendByteArray(const CByteArray& c_byte_array) {
189  /* Send the length of the byte array */
190  UInt32 unSizeNBO = htonl(c_byte_array.Size());
191  SendBuffer(reinterpret_cast<UInt8*>(&unSizeNBO), sizeof(unSizeNBO));
192  /* Send the actual data */
193  SendBuffer(c_byte_array.ToCArray(), c_byte_array.Size());
194  }
195 
196  /****************************************/
197  /****************************************/
198 
200  /* Receive the length of the byte array */
201  UInt32 unSizeNBO;
202  if(ReceiveBuffer(reinterpret_cast<UInt8*>(&unSizeNBO), sizeof(unSizeNBO))) {
203  /* Receive the actual data */
204  c_byte_array.Resize(ntohl(unSizeNBO));
205  if(ReceiveBuffer(c_byte_array.ToCArray(), c_byte_array.Size())) {
206  return true;
207  }
208  }
209  return false;
210  }
211 
212  /****************************************/
213  /****************************************/
214 
215 }
signed int SInt32
32-bit signed integer.
Definition: datatypes.h:93
bool ReceiveByteArray(CByteArray &c_byte_array)
Receives the passed byte array through the socket.
Definition: tcp_socket.cpp:199
void Listen(SInt32 n_port, SInt32 n_queue_length=10)
Listens for connections on the specified local port.
Definition: tcp_socket.cpp:72
void SendByteArray(const CByteArray &c_byte_array)
Sends the passed byte array through the socket.
Definition: tcp_socket.cpp:188
size_t Size() const
Returns the current size of the byte array.
Definition: byte_array.h:66
#define THROW_ARGOSEXCEPTION(message)
This macro throws an ARGoS exception with the passed message.
unsigned char UInt8
8-bit unsigned integer.
Definition: datatypes.h:60
void Resize(size_t un_size, UInt8 un_value=0)
Resizes the byte array to the wanted size.
Definition: byte_array.h:83
void SendBuffer(const UInt8 *pun_buffer, size_t un_size)
Sends the passed buffer through the socket.
Definition: tcp_socket.cpp:152
bool ReceiveBuffer(UInt8 *pun_buffer, size_t un_size)
Fills the passed buffer with the data received through the socket.
Definition: tcp_socket.cpp:169
CTCPSocket(int n_stream=-1)
Definition: tcp_socket.cpp:18
unsigned int UInt32
32-bit unsigned integer.
Definition: datatypes.h:97
void Disconnect()
Close the socket.
Definition: tcp_socket.cpp:143
void Connect(const std::string &str_hostname, SInt32 n_port)
Connects this socket to the specified hostname and port.
Definition: tcp_socket.cpp:32
std::string ToString(const T &t_value)
Converts the given parameter to a std::string.
Byte array utility class.
Definition: byte_array.h:28
const UInt8 * ToCArray() const
Returns the contents of the byte array as a const c-style array.
Definition: byte_array.h:112
The namespace containing all the ARGoS related code.
Definition: ci_actuator.h:12
void Accept(CTCPSocket &c_socket)
Accept a connection from a client.
Definition: tcp_socket.cpp:127