tcp.cpp
Go to the documentation of this file.
1 //
2 // Tcp.cpp
3 //
4 // TCP-Client.
5 //
6 
7 #include "sick_scan/tcp/tcp.hpp"
10 #include <stdio.h> // for sprintf()
11 
12 #include <sys/socket.h> // for socket(), bind(), and connect()
13 #include <arpa/inet.h> // for sockaddr_in and inet_ntoa()
14 #include <string.h> // for memset()
15 #include <netdb.h> // for hostent
16 #include <iostream> // for cout
17 #ifndef _MSC_VER
18 #include <sys/poll.h>
19 #include <poll.h>
20 #endif
21 
23 {
24  m_beVerbose = false;
25  m_connectionSocket = -1;
26 
28 
30  m_disconnectFunction = NULL;
32  m_readFunction = NULL;
33  m_readFunctionObjPtr = NULL;
34 
35 }
36 
37 //
38 // Destruktor.
39 //
40 Tcp::~Tcp(void)
41 {
42  close();
43 }
44 
45 
46 //
47 // Schreibe eine Anzahl Bytes auf die Schnittstelle.
48 //
49 // Dieser Aufruf wartet, bis alle Bytes geschrieben wurden.
50 //
51 bool Tcp::write(UINT8* buffer, UINT32 numberOfBytes)
52 {
53  INT32 bytesSent;
54  bool result;
55 #ifdef _MSC_VER
56  SOCKET* socketPtr = &m_connectionSocket;
57  bytesSent = ::send(*socketPtr, (const char*)buffer, numberOfBytes, 0);
58 #else
59  INT32* socketPtr = &m_connectionSocket;
60  bytesSent = ::send(*socketPtr, buffer, numberOfBytes, 0);
61 #endif
62  // Sende Daten an das Socket
63  if (bytesSent != (INT32)numberOfBytes)
64  {
65  printWarning("Tcp::write: Failed to send data to socket.");
66  result = false;
67  }
68  else
69  {
70  // Erfolg
71  printInfoMessage("Tcp::write: Sent " + toString(numberOfBytes) + " bytes to client.", m_beVerbose);
72  result = true;
73  }
74 
75  return result;
76 }
77 
78 
79 //
80 // Setzt die Funktion, die bei einem Disconnect-Ereignis aufgerufen wird.
81 //
83 {
84  m_disconnectFunction = discFunction;
86 }
87 
88 
89 
96 {
97  if (m_connectionSocket >= 0)
98  {
99 // printInfoMessage("Tcp::isOpen: Reporting open connection.", m_beVerbose);
100  return true;
101  }
102 
103 // printInfoMessage("Tcp::isOpen: Reporting no connection.", m_beVerbose);
104  return false;
105 }
106 
107 //
108 // Definiere die Lese-Callback-Funktion.
109 //
110 void Tcp::setReadCallbackFunction(Tcp::ReadFunction readFunction, void* obj)
111 {
112  m_readFunction = readFunction;
113  m_readFunctionObjPtr = obj;
114 }
115 
116 //
117 // Alternative open-Funktion.
118 //
119 bool Tcp::open(UINT32 ipAddress, UINT16 port, bool enableVerboseDebugOutput)
120 {
121  std::string ipAdrStr;
122 
123  ipAdrStr = ipAdrToString(ipAddress);
124 
125  bool result = open(ipAdrStr, port, enableVerboseDebugOutput);
126 
127  return result;
128 }
129 
130 
131 //
132 // Oeffnet die Verbindung.
133 //
134 // -- Wir sind der Client, und wollen uns z.B. mit einem Scanner verbinden --
135 //
136 bool Tcp::open(std::string ipAddress, UINT16 port, bool enableVerboseDebugOutput)
137 {
138  INT32 result;
139  m_beVerbose = enableVerboseDebugOutput;
140 
141 // printInfoMessage("Tcp::open: Setting up input buffer with size=" + convertValueToString(requiredInputBufferSize) + " bytes.", m_beVerbose);
142 // m_inBuffer.init(requiredInputBufferSize, m_beVerbose);
143 
144  printInfoMessage("Tcp::open: Opening connection.", m_beVerbose);
145 
146  // Socket erzeugen
147  m_connectionSocket = -1; // Keine Verbindung
148  {
149  ScopedLock lock(&m_socketMutex); // Mutex setzen
150  m_connectionSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
151  }
152  if (m_connectionSocket < 0)
153  {
154  printError("Tcp::open: socket() failed, aborting.");
155  return false;
156  }
157 
158  // Socket ist da. Nun die Verbindung oeffnen.
159  printInfoMessage("Tcp::open: Connecting. Target address is " + ipAddress + ":" + toString(port) + ".", m_beVerbose);
160 
161  struct sockaddr_in addr;
162  struct hostent *server;
163  server = gethostbyname(ipAddress.c_str());
164  memset(&addr, 0, sizeof(addr)); // Zero out structure
165  addr.sin_family = AF_INET;
166 #ifdef _MSC_VER
167  memcpy((char *)&addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
168 #else
169  bcopy((char *)server->h_addr, (char *)&addr.sin_addr.s_addr, server->h_length);
170 #endif
171  addr.sin_port = htons(port); // Host-2-Network byte order
172  result = connect(m_connectionSocket, (sockaddr*)(&addr), sizeof(addr));
173  if (result < 0)
174  {
175  // Verbindungsversuch ist fehlgeschlagen
176  std::string text = "Tcp::open: Failed to open TCP connection to " + ipAddress + ", aborting.";
177  printError(text);
178  return false;
179  }
180 
181  printInfoMessage("Tcp::open: Connection established. Now starting read thread.", m_beVerbose);
182 
183  // Empfangsthread starten
184  m_readThread.run(this);
185 
186  printInfoMessage("Tcp::open: Done, leaving now.", m_beVerbose);
187 
188  return true;
189 }
190 
191 
192 //
193 // Lese-Thread (Hauptfunktion).
194 //
195 void Tcp::readThreadFunction(bool& endThread, UINT16& waitTimeMs)
196 {
197  INT32 result;
198 
199  // Lesen
200  result = readInputData();
201 
202  // Ergebnis?
203  if (result < 0)
204  {
205  // Verbindung wurde abgebrochen
206  if (m_readThread.m_threadShouldRun == true)
207  {
208  // Wir sollten eigentlich noch laufen!
209  printInfoMessage("Tcp::readThreadMain: Connection is lost! Read thread terminates now.", m_beVerbose);
210  endThread = true; // interrupt thread
211  }
212  waitTimeMs = 0;
213  }
214  else if (result == 0)
215  {
216  // Wir haben nichts empfangen. Schlafen und dann weiter...
217  waitTimeMs = 1;
218  }
219  else
220  {
221  // Wir haben etwas empfangen, also nicht schlafen
222  waitTimeMs = 0;
223  }
224 }
225 
226 //
227 // Read some data from the TCP connection.
228 //
230 {
231  // Prepare the input buffer
232  const UINT16 max_length = 8192;
233  UINT8 inBuffer[max_length];
234  INT32 recvMsgSize = 0;
235 
236  // Ist die Verbindung offen?
237  if (isOpen() == false)
238  {
239  printError("Tcp::readInputData: Connection is not open, aborting!");
240  return -1;
241  }
242 
243  // Read some data, if any
244 #ifdef _MSC_VER
245  recvMsgSize = recv(m_connectionSocket, (char *)inBuffer, max_length, 0);
246 #else
247  {
248  int ret = -1;
249  do {
250  struct pollfd fd;
251 
252  fd.fd = m_connectionSocket; // your socket handler
253  fd.events = POLLIN;
254  ret = poll(&fd, 1, 1000); // 1 second for timeout
255  switch (ret) {
256  case -1:
257  // Error
258  break;
259  case 0:
260  // Timeout
261  break;
262  default:
263  recvMsgSize = recv(m_connectionSocket, inBuffer, max_length, 0);
264  break;
265  }
266  if (m_readThread.m_threadShouldRun == false)
267  {
268  recvMsgSize = 0;
269  break;
270  }
271  } while (ret == 0);
272  }
273 #endif
274  if (recvMsgSize < 0)
275  {
276  // Fehler
277  printError("Tcp::readInputData: Failed to read data from socket, aborting!");
278  }
279  else if (recvMsgSize > 0)
280  {
281  // Erfolg
282  printInfoMessage("Tcp::readInputData: Read " + toString(recvMsgSize) + " bytes from the connection.", m_beVerbose);
283 
284  // Falls eine Callback-Funktion definiert ist, rufe sie auf mit den
285  // empfangenen Daten.
286  if (m_readFunction != NULL)
287  {
288  // Die Daten an die Callback-Funktion uebergeben
289  UINT32 length_uint32 = (UINT32)recvMsgSize;
290  m_readFunction(m_readFunctionObjPtr, inBuffer, length_uint32);
291  }
292  else
293  {
294  // Es ist keine Callback-Funktion definiert, also die Daten im
295  // lokalen Puffer speichern.
296  for (INT32 i = 0; i < recvMsgSize; i++)
297  {
298  m_rxBuffer.push_back(inBuffer[i]);
299  }
300  }
301  }
302  else if (recvMsgSize == 0)
303  {
304  // Verbindungsabbruch
305  printInfoMessage("Tcp::readInputData: Read 0 bytes - connection is lost!", true);
306 
307  // Informieren?
308  if (m_disconnectFunction != NULL)
309  {
311  }
312 
313  // Mutex setzen
314  ScopedLock lock(&m_socketMutex);
315 
316  m_connectionSocket = -1; // Keine Verbindung mehr
317  }
318 
319  return recvMsgSize;
320 }
321 
322 
323 //
324 // Close an open connection, if any.
325 //
327 {
328  printInfoMessage("Tcp::close: Closing Tcp connection.", m_beVerbose);
329 
330  if (isOpen() == true)
331  {
332  // Dem Lese-Thread ein Ende signalisieren
334 #ifdef _MSC_VER
335  closesocket(m_connectionSocket); // waere evtl. auch fuer Linux korrekt
336 #else
337  // Verbindung schliessen
339 #endif
340  // Auf das Ende des Empfangsthreads warten
341  printInfoMessage("Tcp::close: Waiting for the server thread to terminate...", m_beVerbose);
342 
343  // Thread stoppen
344  stopReadThread();
345  }
346  else
347  {
348  printInfoMessage("Tcp::close: Nothing to do - no open connection? Aborting.", m_beVerbose);
349  }
350 
351  printInfoMessage("Tcp::close: Done - Connection is now closed.", m_beVerbose);
352 }
353 
354 
359 {
360  printInfoMessage("Tcp::stopReadThread: Stopping thread.", m_beVerbose);
361 
363  m_readThread.join();
364 
365  printInfoMessage("Tcp::stopReadThread: Done - Read thread is now closed.", m_beVerbose);
366 }
367 
368 
369 
376 {
377  return (UINT32)m_rxBuffer.size();
378 }
379 
380 
381 //
382 // Read function.
383 //
384 // 0..bufferLen bytes are returned.
385 // Return value is the number of returned bytes.
386 //
387 // DEPRECATED. Use the callback mechanism instead!
388 //
389 UINT32 Tcp::read(UINT8* buffer, UINT32 bufferLen)
390 {
391  UINT32 bytesRead = 0;
392 
393  // Lesen
394  while ((getNumReadableBytes() > 0) && (bufferLen > bytesRead))
395  {
396  buffer[bytesRead] = m_rxBuffer.front();
397  m_rxBuffer.pop_front();
398  bytesRead += 1; // m_inBuffer.read(buffer, bufferLen);
399  }
400 
401  return bytesRead;
402 }
403 
404 
410 std::string Tcp::readString(UINT8 delimiter)
411 {
412  UINT8 c = delimiter;
413  std::string outString;
414  const UINT16 maxStringLength = 8192;
415 
416  // String fuellen
417  while (m_rxBuffer.size() > 0)
418  {
419  // Es sind noch Daten im Puffer
420  c = m_rxBuffer.front();
421  m_rxBuffer.pop_front();
422  if (c == delimiter)
423  {
424  // Trennzeichen gefunden - wir sind fertig!
425  outString = m_rxString;
426  m_rxString.clear();
427  break;
428  }
429  m_rxString += c;
430  }
431 
432  // Ueberlauf der Ausgabe?
433  if (m_rxString.length() > maxStringLength)
434  {
435  if (m_longStringWarningPrinted == false)
436  {
437  // Die lange Version
438  printWarning("Receive-String has excessive length (" + toString(m_rxString.length()) +" bytes). Clearing string. On serial devices, incorrect bitrate settings may cause this behaviour.");
440  }
441  else
442  {
443  // Die Kurzfassung
444  printWarning("Receive-String has excessive length (" + toString(m_rxString.length()) +" bytes). Clearing string.");
445  }
446  m_rxString.clear();
447  }
448 
449  // Textmeldung
450  if ((m_beVerbose == true) && (outString.length() > 0))
451  {
452  printInfoMessage("Tcp::readString: Returning string: " + outString, true);
453  }
454 
455  return outString;
456 }
457 
458 
UINT32 getNumReadableBytes()
Definition: tcp.cpp:375
void printError(std::string message)
bool m_longStringWarningPrinted
Definition: tcp.hpp:58
uint16_t UINT16
void(* DisconnectFunction)(void *obj)
Definition: tcp.hpp:53
#define printInfoMessage(a, b)
uint32_t UINT32
void * m_disconnectFunctionObjPtr
Definition: tcp.hpp:82
bool write(UINT8 *buffer, UINT32 numberOfBytes)
Definition: tcp.cpp:51
SickThread< Tcp,&Tcp::readThreadFunction > m_readThread
Definition: tcp.hpp:76
bool m_beVerbose
Definition: tcp.hpp:67
std::string m_rxString
Definition: tcp.hpp:59
std::list< unsigned char > m_rxBuffer
Definition: tcp.hpp:61
bool m_threadShouldRun
Definition: SickThread.hpp:106
void run(void *classptr)
Definition: SickThread.hpp:26
void readThreadFunction(bool &endThread, UINT16 &waitTimeMs)
Definition: tcp.cpp:195
bool open(std::string ipAddress, UINT16 port, bool enableVerboseDebugOutput=false)
Definition: tcp.cpp:136
void * m_readFunctionObjPtr
Definition: tcp.hpp:80
ReadFunction m_readFunction
Definition: tcp.hpp:79
std::string ipAdrToString(UINT32 ipAddress)
Definition: toolbox.cpp:432
void(* ReadFunction)(void *obj, UINT8 *inputBuffer, UINT32 &numBytes)
Definition: tcp.hpp:49
INT32 readInputData()
Definition: tcp.cpp:229
DisconnectFunction m_disconnectFunction
Definition: tcp.hpp:81
std::string readString(UINT8 delimiter)
Definition: tcp.cpp:410
int32_t INT32
Mutex m_socketMutex
Definition: tcp.hpp:68
std::string toString(INT32 value)
Definition: toolbox.cpp:279
~Tcp()
Definition: tcp.cpp:40
bool isOpen()
Definition: tcp.cpp:95
Tcp()
Definition: tcp.cpp:22
void join()
Definition: SickThread.hpp:99
UINT32 read(UINT8 *buffer, UINT32 bufferLen)
Definition: tcp.cpp:389
void setReadCallbackFunction(ReadFunction readFunction, void *obj)
Definition: tcp.cpp:110
void setDisconnectCallbackFunction(DisconnectFunction discFunction, void *obj)
Definition: tcp.cpp:82
void close()
Definition: tcp.cpp:326
INT32 m_connectionSocket
Definition: tcp.hpp:70
void printWarning(std::string message)
void stopReadThread()
Definition: tcp.cpp:358
uint8_t UINT8


sick_scan
Author(s): Michael Lehning , Jochen Sprickerhof , Martin Günther
autogenerated on Wed May 5 2021 03:05:48