ARGoS 3
A parallel, multi-engine simulator for swarm robotics
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
15namespace 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
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
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
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}
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
#define THROW_ARGOSEXCEPTION(message)
This macro throws an ARGoS exception with the passed message.
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.
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.
void SendByteArray(const CByteArray &c_byte_array)
Sends the passed byte array through the socket.
bool ReceiveByteArray(CByteArray &c_byte_array)
Receives the passed byte array through the socket.
CTCPSocket(int n_stream=-1)
CTCPSocket & operator=(const CTCPSocket &c_other)=delete
void Disconnect()
Close the socket.
void SendMsg(const CByteArray &c_payload, bool b_more=false)
void RecvMsg(CByteArray &c_payload)
void Listen(SInt32 n_port, SInt32 n_queue_length=10)
Listens for connections on the specified local port.
void SendBuffer(const UInt8 *pun_buffer, size_t un_size)
Sends the passed buffer through the socket.
std::unordered_set< EEvent > GetEvents()
Check the socket for events.
bool ReceiveBuffer(UInt8 *pun_buffer, size_t un_size)
Fills the passed buffer with the data received through the socket.
void Connect(const std::string &str_hostname, SInt32 n_port)
Connects this socket to the specified hostname and port.