PageRenderTime 25ms CodeModel.GetById 45ms RepoModel.GetById 1ms app.codeStats 0ms

/src/cpp/tcp/TcpSocket.cpp

https://gitlab.com/F34140r/robotino-brain
C++ | 378 lines | 263 code | 75 blank | 40 comment | 55 complexity | cba97ab1536d0c3834d832256affffeb MD5 | raw file
  1. #include "TcpSocket.h"
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netdb.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <string>
  10. #include <sstream>
  11. #include <stdexcept>
  12. #define BUF_SIZE 500
  13. #define DEBUG false
  14. /**
  15. * @author s171170 Lars Øyvind Hagland
  16. */
  17. void TcpSocket::debug(const char message[], std::string output)
  18. {
  19. if (DEBUG)
  20. {
  21. fprintf(stderr, "TcpSocket-Debug");
  22. (isServer)?
  23. fprintf(stderr, "Server: "):
  24. fprintf(stderr, "Client: ");
  25. fprintf(stderr, "%s\n", message);
  26. if (!output.empty()) fprintf(stderr, "%s\n", output.c_str());
  27. }
  28. }
  29. TcpSocket::TcpSocket()
  30. {
  31. this->isServer = false;
  32. this->_isConnected = false;
  33. }
  34. /**
  35. * @author s171170 Lars Øyvind Hagland
  36. * Based on getaddrinfo() manpage's example
  37. * server and client
  38. */
  39. TcpSocket::TcpSocket(char port[])
  40. {
  41. isServer = true;
  42. _isConnected = false;
  43. debug("Creating server");
  44. struct addrinfo hints;
  45. struct addrinfo *result, *rp;
  46. struct sockaddr_storage _peer_addr;
  47. this->peer_addr = &_peer_addr;
  48. memset(&hints, 0, sizeof(struct addrinfo));
  49. hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
  50. hints.ai_socktype = SOCK_STREAM;/* Stream socket */
  51. hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
  52. hints.ai_protocol = 6; /* TCP protocol */
  53. hints.ai_canonname = NULL;
  54. hints.ai_addr = NULL;
  55. hints.ai_next = NULL;
  56. int s = getaddrinfo(NULL, port, &hints, &result);
  57. if (s != 0) {
  58. // std::string errormsg
  59. // throw new std::runtime_error( "getaddrinfo: " << gai_strerrror( s ) );
  60. // fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
  61. exit(EXIT_FAILURE);
  62. }
  63. for (rp = result; rp != NULL; rp = rp->ai_next) {
  64. socketFD = socket(rp->ai_family, rp->ai_socktype,
  65. rp->ai_protocol);
  66. if (socketFD == -1)
  67. continue;
  68. if (bind(socketFD, rp->ai_addr, rp->ai_addrlen) == 0)
  69. {
  70. debug("Bind success");
  71. break; /* Success */
  72. }
  73. ::close(socketFD);
  74. }
  75. if (rp == NULL) { /* No address succeeded */
  76. /// @todo throw exception instead of stderr+exit()
  77. fprintf(stderr, "Could not bind\n");
  78. exit(EXIT_FAILURE);
  79. }
  80. freeaddrinfo(result); /* No longer needed */
  81. if (listen(socketFD, 5) == -1)
  82. {
  83. /// @todo throw exception instead of stderr+exit()
  84. fprintf(stderr, "Could not listen\n");
  85. exit(EXIT_FAILURE);
  86. }
  87. debug("Listening");
  88. peer_addr_len = sizeof(struct sockaddr_storage);
  89. }
  90. /**
  91. * @author s171170 Lars Øyvind Hagland
  92. * Based on getaddrinfo() manpage's example
  93. * server and client
  94. */
  95. void TcpSocket::client(char port[], char host[])
  96. {
  97. isServer = false;
  98. _isConnected = false;
  99. debug("Creating client");
  100. struct addrinfo hints;
  101. struct addrinfo *result, *rp;
  102. /* Obtain address(es) matching host/port */
  103. memset(&hints, 0, sizeof(struct addrinfo));
  104. hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
  105. hints.ai_socktype = SOCK_STREAM;/* Stream socket */
  106. hints.ai_flags = 0;
  107. hints.ai_protocol = 6; /* TCP protocol */
  108. int s = getaddrinfo(host, port, &hints, &result);
  109. if (s != 0) {
  110. fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
  111. exit(EXIT_FAILURE);
  112. }
  113. for (rp = result; rp != NULL; rp = rp->ai_next)
  114. {
  115. socketFD = socket(rp->ai_family, rp->ai_socktype,
  116. rp->ai_protocol);
  117. if (socketFD == -1)
  118. continue;
  119. if (connect(socketFD, rp->ai_addr, rp->ai_addrlen) != -1)
  120. {
  121. debug("Connected");
  122. break; /* Success */
  123. }
  124. ::close(socketFD);
  125. }
  126. if (rp == NULL) { /* No address succeeded */
  127. /// @todo throw exception instead of stderr+exit()
  128. fprintf(stderr, "Could not connect\n");
  129. exit(EXIT_FAILURE);
  130. }
  131. _isConnected = true;
  132. connectionFD = socketFD;
  133. freeaddrinfo(result); /* No longer needed */
  134. }
  135. TcpSocket::TcpSocket(char port[], char host[])
  136. {
  137. client( port, host);
  138. }
  139. TcpSocket::TcpSocket( std::string port, std::string server )
  140. {
  141. int portSize = port.size();
  142. char charPort[ portSize + 1];
  143. int i = 0;
  144. while ( i <= portSize )
  145. {
  146. charPort[ i ] = port[ i ];
  147. i++;
  148. }
  149. charPort[ i ] = '\0';
  150. int serverSize = server.size();
  151. char charServer[ serverSize + 1];
  152. i = 0;
  153. while ( i <= serverSize )
  154. {
  155. charServer[ i ] = server[ i ];
  156. i++;
  157. }
  158. charServer[ i ] = '\0';
  159. if ( DEBUG )
  160. {
  161. std::stringstream connection;
  162. connection << charServer << ":" << charPort;
  163. debug( "Connecting to: ", connection.str() );
  164. }
  165. client( charPort, charServer );
  166. }
  167. /**
  168. * @author s171170 Lars Øyvind Hagland
  169. * Based on getaddrinfo() manpage's example
  170. * server and client
  171. */
  172. bool TcpSocket::accept()
  173. {
  174. do
  175. {
  176. connectionFD =
  177. ::accept(
  178. socketFD,
  179. (struct sockaddr *) &peer_addr,
  180. &peer_addr_len);
  181. } while (connectionFD == -1);
  182. debug("Connection accepted");
  183. _isConnected = true;
  184. return true;
  185. }
  186. /**
  187. * @author s171170 Lars Øyvind Hagland
  188. */
  189. bool TcpSocket::read(std::string& recepticle, std::string endTag)
  190. {
  191. debug("Reading from socket");
  192. char
  193. buffer[BUF_SIZE+1];
  194. int
  195. length = 1,
  196. total = 0,
  197. noRead = 0,
  198. endTagLength = 0;
  199. std::stringstream
  200. ss;
  201. while (true)
  202. {
  203. bzero(buffer, BUF_SIZE+1);
  204. length = ::recv(connectionFD, buffer, BUF_SIZE, 0);
  205. noRead++;
  206. debug("Recieved");
  207. if (length < 0)
  208. {
  209. /// @todo throw exception instead of stderr+exit()
  210. fprintf(stderr, "Error reading from socket\n");
  211. // exit(1);
  212. return false;
  213. }
  214. total += length;
  215. buffer[length] = '\0';
  216. ss << buffer;
  217. if (endTag.empty() || (noRead == 1 && length < BUF_SIZE))
  218. {
  219. recepticle.assign(ss.str());
  220. break;
  221. }
  222. if (endTagLength == 0) endTagLength = endTag.size();
  223. int i = 0;
  224. while (endTagLength > 0 && length > 0)
  225. {
  226. if (buffer[--length] != endTag[--endTagLength]) continue;
  227. i++;
  228. }
  229. recepticle.assign(ss.str());
  230. if (endTagLength > 0)
  231. {
  232. i = recepticle.size() - i;
  233. while(endTagLength > 0 && i > 0)
  234. {
  235. if (recepticle[--i] != endTag[--endTagLength]) continue;
  236. }
  237. }
  238. break;
  239. }
  240. debug("Read done, reciever:", recepticle);
  241. return (total != 0);
  242. }
  243. /**
  244. * @author s171170 Lars Øyvind Hagland
  245. */
  246. bool TcpSocket::write(std::string& message)
  247. {
  248. debug("Writing to socket:", message);
  249. int
  250. pos = 0,
  251. cut,
  252. length = message.size(),
  253. writeLen;
  254. char
  255. buffer[BUF_SIZE];
  256. while (pos < length)
  257. {
  258. bzero(buffer, BUF_SIZE);
  259. cut = ((pos + BUF_SIZE) < length) ?
  260. BUF_SIZE :
  261. length - pos;
  262. for (int i = 0; i < cut; i++) buffer[i] = message[pos + i];
  263. writeLen = ::send(connectionFD, buffer, cut, 0);
  264. pos = cut;
  265. if (writeLen < 0)
  266. {
  267. /// @todo throw exception instead of stderr+exit()
  268. fprintf(stderr, "Error writing to buffer\n");
  269. exit(1);
  270. }
  271. if (writeLen != cut)
  272. {
  273. fprintf(stderr,
  274. "Could only write %i chars, %i tried\n",
  275. writeLen, cut);
  276. exit(1);
  277. }
  278. }
  279. debug("Write done");
  280. return true;
  281. }
  282. bool TcpSocket::isConnected()
  283. {
  284. return this->_isConnected;
  285. }
  286. /**
  287. * @author s171170 Lars Øyvind Hagland
  288. */
  289. bool TcpSocket::close()
  290. {
  291. debug("Closing socket(s)");
  292. if (isServer && _isConnected) ::close(connectionFD);
  293. if (_isConnected && ::close(socketFD)) return true;
  294. return false;
  295. }
  296. /**
  297. * @author s171170 Lars Øyvind Hagland
  298. */
  299. TcpSocket::~TcpSocket()
  300. {
  301. debug("Destructing");
  302. this->close();
  303. }