You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

networking.cpp 61KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. // networking.cpp
  2. //
  3. // Copyright (C) 2004-2020 MicroNeil Research Corporation.
  4. //
  5. // This software is released under the MIT license. See LICENSE.TXT.
  6. #include "networking.hpp"
  7. namespace codedweller {
  8. Networking Network; // Creating _THE_ Network instance.
  9. #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
  10. int Networking::getLastError() { // In windows you get the last error
  11. return WSAGetLastError(); // from WSAGetLastError();
  12. }
  13. int Networking::setNonBlocking(hSocket socket) { // Set a winsock to non-blocking
  14. unsigned long nonblocking = 1; // Create a flag...
  15. int result = 0;
  16. if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) { // Set the state of the socket.
  17. result = -1; // If that fails then return -1.
  18. }
  19. return result; // Show 'em my motto!
  20. }
  21. int Networking::closeSocket(hSocket socket) { // Close a socket in winsock
  22. return closesocket(socket); // wraps closesocket().
  23. }
  24. bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK.
  25. return (WSAEWOULDBLOCK == ErrorCode);
  26. }
  27. bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS.
  28. return( // [WSA]EALREADY also returns true.
  29. WSAEINPROGRESS == ErrorCode || // In fact, on Win* platforms we could
  30. WSAEALREADY == ErrorCode || // get any of these when retesting
  31. WSAEWOULDBLOCK == ErrorCode || // open() for a connection.
  32. WSAEINVAL == ErrorCode
  33. );
  34. }
  35. bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN.
  36. return(WSAEISCONN == ErrorCode);
  37. }
  38. #else
  39. //// GNU platform
  40. int Networking::getLastError() { // In GNU you get the last error
  41. return errno; // from errno;
  42. }
  43. int Networking::setNonBlocking(hSocket socket) { // Set a socket to non-blocking
  44. int flags, result; // Grab a place to hold the flags.
  45. flags = fcntl(socket, F_GETFL, 0); // Get the current flags.
  46. result = fcntl(socket, F_SETFL, flags | O_NONBLOCK); // Set the NONBLOCK flag & return.
  47. return result; // Return the result.
  48. }
  49. int Networking::closeSocket(hSocket socket) { // Close a socket in GNU
  50. return close(socket); // wraps close().
  51. }
  52. bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK.
  53. return (EWOULDBLOCK == ErrorCode);
  54. }
  55. bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS.
  56. return( // [WSA]EALREADY also returns true.
  57. EINPROGRESS == ErrorCode ||
  58. EALREADY == ErrorCode
  59. );
  60. }
  61. bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN.
  62. return(EISCONN == ErrorCode);
  63. }
  64. #endif
  65. // End Platform Specific
  66. ////////////////////////////////////////////////////////////////////////////////
  67. // Begin Platform Agnostic
  68. //// class IP4Address //////////////////////////////////////////////////////////
  69. IP4Address::IP4Address():IP(0){} // Blank constructor IP = 0.0.0.0
  70. IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){} // Constructor given unsigned long
  71. IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){} // Constructor given an IP4Address
  72. IP4Address::IP4Address(const char* newIP) { (*this) = newIP; } // Construcing with a cstring.
  73. IP4Address::IP4Address(const std::string& newIP) { (*this) = newIP; } // Constructing with a cppstring.
  74. IP4Address& IP4Address::operator=(const unsigned long int Right) { // Convert from unsigned long int.
  75. IP = Right;
  76. return *this;
  77. }
  78. IP4Address& IP4Address::operator=(const char* Right) { // Convert from c string.
  79. IP = ntohl(inet_addr(Right));
  80. return *this;
  81. }
  82. IP4Address& IP4Address::operator=(const std::string& Right) { // Convert from cpp string.
  83. IP = ntohl(inet_addr(Right.c_str()));
  84. return *this;
  85. }
  86. bool IP4Address::operator<(const IP4Address Right) const { // < Comparison.
  87. return (IP < Right.IP);
  88. }
  89. bool IP4Address::operator>(const IP4Address Right) const { // > Comparison.
  90. return (IP > Right.IP);
  91. }
  92. bool IP4Address::operator==(const IP4Address Right) const { // == Comparison.
  93. return (IP == Right.IP);
  94. }
  95. bool IP4Address::operator!=(const IP4Address Right) const { // != Comparison.
  96. return (IP != Right.IP);
  97. }
  98. bool IP4Address::operator<=(const IP4Address Right) const { // <= Comparison.
  99. return (IP <= Right.IP);
  100. }
  101. bool IP4Address::operator>=(const IP4Address Right) const { // >= Comparison.
  102. return (IP >= Right.IP);
  103. }
  104. //// class SocketAddress ///////////////////////////////////////////////////////
  105. void SocketAddress::clear() {
  106. memset(&Address, 0, sizeof(Address)); // Zero out the address strcuture
  107. Address.sin_family = AF_INET; // Internet Address Family ip4
  108. Address.sin_addr.s_addr = htonl(INADDR_ANY); // Any IP address
  109. Address.sin_port = 0; // Zero means any port.
  110. }
  111. SocketAddress::SocketAddress() { // Constructor sets up w/ wildcards
  112. clear(); // Conveniently, we can use clear() :-)
  113. }
  114. struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() { // Returns a pointer to sockaddr_in.
  115. return &Address; // Simply return it's address.
  116. }
  117. struct sockaddr* SocketAddress::getPtr_sockaddr() { // Returns a pointer to sockaddr.
  118. return (struct sockaddr*) &Address;
  119. }
  120. socklen_t SocketAddress::getAddressSize() {
  121. return sizeof(Address); // Return the size of the structure.
  122. }
  123. void SocketAddress::setAddress(unsigned long ipAddress) { // Set the IP address from an unsigned int
  124. Address.sin_addr.s_addr = htonl(ipAddress); // Convert to network order and assign.
  125. }
  126. void SocketAddress::setAddress(char* ipString) { // Set the IP address from a cstring
  127. Address.sin_addr.s_addr = inet_addr(ipString); // Convert to number and assign.
  128. }
  129. unsigned long SocketAddress::getAddress() { // Get the IP address as an unsigned int
  130. return ntohl(Address.sin_addr.s_addr); // Convert to host order and return.
  131. }
  132. void SocketAddress::setPort(unsigned short port) { // Set the port address from an int
  133. Address.sin_port = htons(port); // Convert to network order and set.
  134. }
  135. void SocketAddress::setPort(char* port) { // Set the port address from a cstring
  136. setPort(atoi(port)); // Convert to int and set.
  137. }
  138. unsigned short SocketAddress::getPort() { // Get the port address as an unsigned int
  139. return ntohs(Address.sin_port); // Convert to host order and return.
  140. }
  141. const char* SocketAddress::getPort(char* str) { // Get the port address into a cstring.
  142. if(NULL == str) { // If the caller did not provide a
  143. str = PortStringBuffer; // buffer to use then we will use ours.
  144. }
  145. sprintf(str,"%d",getPort()); // Get the port and convert to cstring.
  146. return str; // Return the string we got.
  147. }
  148. //// class Socket //////////////////////////////////////////////////////////////
  149. Socket::Socket() : // When starting up we are
  150. Handle(INVALID_SOCKET), OpenSucceeded(false) { // not yet valid.
  151. }
  152. Socket::~Socket() { // When shutting down, be sure
  153. if(INVALID_SOCKET != Handle) { // any open socket is closed without
  154. Network.closeSocket(Handle); // throwing any exceptions.
  155. }
  156. }
  157. void Socket::close() { // When we close,
  158. if(INVALID_SOCKET != Handle) { // If the handle is open then
  159. if(Network.closeSocket(Handle)) { // close the handle and check for error.
  160. LastError = Network.getLastError(); // If there was an error record it.
  161. if(!Network.WouldBlock(LastError)) { // If the error was not WOULDBLOCK
  162. throw Networking::ControlError( // then throw a ControlError exception.
  163. Network.DescriptiveError(
  164. "Socket::close()", LastError));
  165. }
  166. } else { // If there was no error then
  167. LastError = 0; // reset the LastError value.
  168. }
  169. Handle = INVALID_SOCKET; // and reset the handle to INVALID.
  170. NonBlocking = false; // The default is Blocking.
  171. OpenSucceeded = false; // After close, forget we opened.
  172. }
  173. }
  174. hSocket Socket::getHandle() { // Returns the current Socket handle.
  175. return Handle;
  176. }
  177. bool Socket::isNonBlocking() { // Returns true if socket is NonBlocking
  178. return NonBlocking;
  179. }
  180. void Socket::makeNonBlocking() { // Sets the socket to NonBlocking mode.
  181. if(0 > Network.setNonBlocking(Handle)) { // Feed the call through Network.
  182. LastError = Network.getLastError(); // If it didn't work, go get the error.
  183. NonBlocking = false; // We are NOT NonBlocking.
  184. throw Networking::ControlError( // Throw a control error.
  185. Network.DescriptiveError(
  186. "Socket::makeNonBlocking()", LastError));
  187. } else {
  188. NonBlocking = true; // If we didn't throw, we're ON.
  189. }
  190. }
  191. bool Socket::isReuseAddress() { return ReuseAddress; } // True if socket is set SO_REUSEADDR.
  192. bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); } // Changes SO_REUSEADDR setting.
  193. bool Socket::isOpen() { // True if the socket is open.
  194. return(
  195. INVALID_SOCKET != Handle && // A valid handle and
  196. true == OpenSucceeded // a successful open operation
  197. ); // means we're open.
  198. }
  199. int Socket::getLastError() { // Returns the last error for this socket.
  200. return LastError;
  201. }
  202. //// class TCPClient ///////////////////////////////////////////////////////////
  203. TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) : // How to create a TCPClient.
  204. MyListener(L) { // Capture our listener.
  205. Handle = H; // Capture the new socket handle.
  206. RemoteAddress = A; // Capture the client address.
  207. ReadPointer = ReadBuffer; // Set the read position to zero.
  208. DataLength = 0; // There is no data yet.
  209. OpenSucceeded = true; // We're getting an open socket.
  210. }
  211. TCPClient::~TCPClient() { // When destroying a TCPClient
  212. try{ if(isOpen()) close(); } catch(...) {} // silently close any open connections.
  213. }
  214. void TCPClient::open() { // We provide open() as unsupported.
  215. throw Networking::NotSupportedError( // Throw an exception if this is called.
  216. Network.DescriptiveError(
  217. "TCPClient::open()", LastError));
  218. }
  219. bool TCPClient::ReadBufferIsEmpty() { // True if the ReadBuffer is empty.
  220. return (0 >= DataLength); // We can check that with DataLength.
  221. }
  222. void TCPClient::fillReadBuffer() { // Fills the buffer from the socket.
  223. LastError = 0; // Clear the LastError value.
  224. ReadPointer = ReadBuffer; // Reset the ReadPointer.
  225. DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data.
  226. if(0 >= DataLength) { // If there was an error then
  227. LastError = Network.getLastError(); // Grab the last error code.
  228. DataLength = 0; // Correct the DataLength.
  229. if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
  230. return; // simply return - it's ok.
  231. } else { // If it was a different error
  232. throw Networking::SocketReadError( // then throw a ReadError.
  233. Network.DescriptiveError(
  234. "TCPClient::fillReadBuffer()", LastError));
  235. }
  236. } // If we succeeded then our ReadBuffer
  237. } // assembly is in good shape.
  238. bool TCPClient::isNonBlocking() { // Provided for MessagePort.
  239. return Socket::isNonBlocking();
  240. }
  241. unsigned long TCPClient::getRemoteIP() { // Get remote IP as long.
  242. return RemoteAddress.getAddress();
  243. }
  244. const char* TCPClient::getRemoteIP(char* str) { // Get IP as string.
  245. return RemoteAddress.getAddress(str);
  246. }
  247. unsigned short TCPClient::getRemotePort() { // Get remote Port as unsigned short.
  248. return RemoteAddress.getPort();
  249. }
  250. const char* TCPClient::getRemotePort(char* str) { // Get Port as string.
  251. return RemoteAddress.getPort(str);
  252. }
  253. //// class TCPHost /////////////////////////////////////////////////////////////
  254. TCPHost::~TCPHost() { // When destroying a TCPHost
  255. try{ if(isOpen()) close(); } catch(...) {} // silently close any open connection.
  256. }
  257. bool TCPHost::ReadBufferIsEmpty() { // True if the ReadBuffer is empty.
  258. return (0 >= DataLength); // We can check that with DataLength.
  259. }
  260. void TCPHost::fillReadBuffer() { // Fills the buffer from the socket.
  261. LastError = 0; // Clear the LastError value.
  262. ReadPointer = ReadBuffer; // Reset the ReadPointer.
  263. DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data.
  264. if(0 >= DataLength) { // If there was an error then
  265. LastError = Network.getLastError(); // Grab the last error code.
  266. DataLength = 0; // Correct the DataLength.
  267. if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
  268. return; // simply return - it's ok.
  269. } else { // If it was a different error
  270. throw Networking::SocketReadError( // then throw a ReadError.
  271. Network.DescriptiveError(
  272. "TCPHost::fillReadBuffer()", LastError));
  273. }
  274. } // If we succeeded then our ReadBuffer
  275. } // assembly is in good shape.
  276. bool TCPHost::isNonBlocking() { // Provided for MessagePort.
  277. return Socket::isNonBlocking();
  278. }
  279. //// class TCPListener /////////////////////////////////////////////////////////
  280. TCPListener::~TCPListener() { // When destroying a TCPListener
  281. try{ close(); } catch(...) {} // silently close if not already done.
  282. }
  283. //// Platform Specific Stuff ///////////////////////////////////////////////////
  284. #if defined(WIN32) || defined(WIN64)
  285. ////////////////////////////////////////////////////////////////////////////////
  286. //// Being Windows specific code
  287. WSADATA WSSTartData; // Socket library data structure.
  288. // Error description handling for humans.
  289. std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno.
  290. std::string s = ""; // Message string.
  291. switch(Errno) { // Assign the appropriate message.
  292. case WSA_INVALID_HANDLE: s = "WSA_INVALID_HANDLE"; break;
  293. case WSA_NOT_ENOUGH_MEMORY: s = "WSA_NOT_ENOUGH_MEMORY"; break;
  294. case WSA_INVALID_PARAMETER: s = "WSA_INVALID_PARAMETER"; break;
  295. case WSA_OPERATION_ABORTED: s = "WSA_OPERATION_ABORTED"; break;
  296. case WSA_IO_INCOMPLETE: s = "WSA_IO_INCOMPLETE"; break;
  297. case WSA_IO_PENDING: s = "WSA_IO_PENDING"; break;
  298. case WSAEINTR: s = "WSAEINTR"; break;
  299. case WSAEBADF: s = "WSAEBADF"; break;
  300. case WSAEACCES: s = "WSAEACCES"; break;
  301. case WSAEFAULT: s = "WSAEFAULT"; break;
  302. case WSAEINVAL: s = "WSAEINVAL"; break;
  303. case WSAEMFILE: s = "WSAEMFILE"; break;
  304. case WSAEWOULDBLOCK: s = "WSAEWOULDBLOCK"; break;
  305. case WSAEINPROGRESS: s = "WSAEINPROGRESS"; break;
  306. case WSAEALREADY: s = "WSAEALREADY"; break;
  307. case WSAENOTSOCK: s = "WSAENOTSOCK"; break;
  308. case WSAEDESTADDRREQ: s = "WSAEDESTADDRREQ"; break;
  309. case WSAEMSGSIZE: s = "WSAEMSGSIZE"; break;
  310. case WSAEPROTOTYPE: s = "WSAEPROTOTYPE"; break;
  311. case WSAENOPROTOOPT: s = "WSAENOPROTOOPT"; break;
  312. case WSAEPROTONOSUPPORT: s = "WSAEPROTONOSUPPORT"; break;
  313. case WSAESOCKTNOSUPPORT: s = "WSAESOCKTNOSUPPORT"; break;
  314. case WSAEOPNOTSUPP: s = "WSAEOPNOTSUPP"; break;
  315. case WSAEPFNOSUPPORT: s = "WSAEPFNOSUPPORT"; break;
  316. case WSAEAFNOSUPPORT: s = "WSAEAFNOSUPPORT"; break;
  317. case WSAEADDRINUSE: s = "WSAEADDRINUSE"; break;
  318. case WSAEADDRNOTAVAIL: s = "WSAEADDRNOTAVAIL"; break;
  319. case WSAENETDOWN: s = "WSAENETDOWN"; break;
  320. case WSAENETUNREACH: s = "WSAENETUNREACH"; break;
  321. case WSAENETRESET: s = "WSAENETRESET"; break;
  322. case WSAECONNABORTED: s = "WSAECONNABORTED"; break;
  323. case WSAECONNRESET: s = "WSAECONNRESET"; break;
  324. case WSAENOBUFS: s = "WSAENOBUFS"; break;
  325. case WSAEISCONN: s = "WSAEISCONN"; break;
  326. case WSAENOTCONN: s = "WSAENOTCONN"; break;
  327. case WSAESHUTDOWN: s = "WSAESHUTDOWN"; break;
  328. case WSAETOOMANYREFS: s = "WSAETOOMANYREFS"; break;
  329. case WSAETIMEDOUT: s = "WSAETIMEDOUT"; break;
  330. case WSAECONNREFUSED: s = "WSAECONNREFUSED"; break;
  331. case WSAELOOP: s = "WSAELOOP"; break;
  332. case WSAENAMETOOLONG: s = "WSAENAMETOOLONG"; break;
  333. case WSAEHOSTDOWN: s = "WSAEHOSTDOWN"; break;
  334. case WSAEHOSTUNREACH: s = "WSAEHOSTUNREACH"; break;
  335. case WSAENOTEMPTY: s = "WSAENOTEMPTY"; break;
  336. case WSAEPROCLIM: s = "WSAEPROCLIM"; break;
  337. case WSAEUSERS: s = "WSAEUSERS"; break;
  338. case WSAEDQUOT: s = "WSAEDQUOT"; break;
  339. case WSAESTALE: s = "WSAESTALE"; break;
  340. case WSAEREMOTE: s = "WSAEREMOTE"; break;
  341. case WSASYSNOTREADY: s = "WSASYSNOTREADY"; break;
  342. case WSAVERNOTSUPPORTED: s = "WSAVERNOTSUPPORTED"; break;
  343. case WSANOTINITIALISED: s = "WSANOTINITIALISED"; break;
  344. case WSAEDISCON: s = "WSAEDISCON"; break;
  345. case WSAENOMORE: s = "WSAENOMORE"; break;
  346. case WSAECANCELLED: s = "WSAECANCELLED"; break;
  347. case WSAEINVALIDPROCTABLE: s = "WSAEINVALIDPROCTABLE"; break;
  348. case WSAEINVALIDPROVIDER: s = "WSAEINVALIDPROVIDER"; break;
  349. case WSAEPROVIDERFAILEDINIT: s = "WSAEPROVIDERFAILEDINIT"; break;
  350. case WSASYSCALLFAILURE: s = "WSASYSCALLFAILURE"; break;
  351. case WSASERVICE_NOT_FOUND: s = "WSASERVICE_NOT_FOUND"; break;
  352. case WSATYPE_NOT_FOUND: s = "WSATYPE_NOT_FOUND"; break;
  353. case WSA_E_NO_MORE: s = "WSA_E_NO_MORE"; break;
  354. case WSA_E_CANCELLED: s = "WSA_E_CANCELLED"; break;
  355. case WSAEREFUSED: s = "WSAEREFUSED"; break;
  356. case WSAHOST_NOT_FOUND: s = "WSAHOST_NOT_FOUND"; break;
  357. case WSATRY_AGAIN: s = "WSATRY_AGAIN"; break;
  358. case WSANO_RECOVERY: s = "WSANO_RECOVERY"; break;
  359. case WSANO_DATA: s = "WSANO_DATA"; break;
  360. case WSA_QOS_RECEIVERS: s = "WSA_QOS_RECEIVERS"; break;
  361. case WSA_QOS_SENDERS: s = "WSA_QOS_SENDERS"; break;
  362. case WSA_QOS_NO_SENDERS: s = "WSA_QOS_NO_SENDERS"; break;
  363. case WSA_QOS_NO_RECEIVERS: s = "WSA_QOS_NO_RECEIVERS"; break;
  364. case WSA_QOS_REQUEST_CONFIRMED: s = "WSA_QOS_REQUEST_CONFIRMED"; break;
  365. case WSA_QOS_ADMISSION_FAILURE: s = "WSA_QOS_ADMISSION_FAILURE"; break;
  366. case WSA_QOS_POLICY_FAILURE: s = "WSA_QOS_POLICY_FAILURE"; break;
  367. case WSA_QOS_BAD_STYLE: s = "WSA_QOS_BAD_STYLE"; break;
  368. case WSA_QOS_BAD_OBJECT: s = "WSA_QOS_BAD_OBJECT"; break;
  369. case WSA_QOS_TRAFFIC_CTRL_ERROR: s = "WSA_QOS_TRAFFIC_CTRL_ERROR"; break;
  370. case WSA_QOS_GENERIC_ERROR: s = "WSA_QOS_GENERIC_ERROR"; break;
  371. case WSA_QOS_ESERVICETYPE: s = "WSA_QOS_ESERVICETYPE"; break;
  372. case WSA_QOS_EFLOWSPEC: s = "WSA_QOS_EFLOWSPEC"; break;
  373. case WSA_QOS_EPROVSPECBUF: s = "WSA_QOS_EPROVSPECBUF"; break;
  374. case WSA_QOS_EFILTERSTYLE: s = "WSA_QOS_EFILTERSTYLE"; break;
  375. case WSA_QOS_EFILTERTYPE: s = "WSA_QOS_EFILTERTYPE"; break;
  376. case WSA_QOS_EFILTERCOUNT: s = "WSA_QOS_EFILTERCOUNT"; break;
  377. case WSA_QOS_EOBJLENGTH: s = "WSA_QOS_EOBJLENGTH"; break;
  378. case WSA_QOS_EFLOWCOUNT: s = "WSA_QOS_EFLOWCOUNT"; break;
  379. case WSA_QOS_EPOLICYOBJ: s = "WSA_QOS_EPOLICYOBJ"; break;
  380. case WSA_QOS_EFLOWDESC: s = "WSA_QOS_EFLOWDESC"; break;
  381. case WSA_QOS_EPSFLOWSPEC: s = "WSA_QOS_EPSFLOWSPEC"; break;
  382. case WSA_QOS_EPSFILTERSPEC: s = "WSA_QOS_EPSFILTERSPEC"; break;
  383. case WSA_QOS_ESDMODEOBJ: s = "WSA_QOS_ESDMODEOBJ"; break;
  384. case WSA_QOS_ESHAPERATEOBJ: s = "WSA_QOS_ESHAPERATEOBJ"; break;
  385. case WSA_QOS_RESERVED_PETYPE: s = "WSA_QOS_RESERVED_PETYPE"; break;
  386. #ifdef WSA_QOS_EUNKOWNPSOBJ
  387. case WSA_QOS_EUNKOWNPSOBJ: s = "WSA_QOS_EUNKOWNPSOBJ"; break;
  388. #endif
  389. }
  390. Msg.append(" "); // Add a space to the existing message.
  391. if(0 < s.length()) { // If we know the message for Errno
  392. Msg.append(s); // then append it.
  393. }
  394. else { // If we don't know what Errno means
  395. std::ostringstream ErrNoMsg; // then say so and pass on Errno as
  396. ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno; // well so someone can figure it out.
  397. Msg.append(ErrNoMsg.str());
  398. }
  399. return Msg;
  400. };
  401. // Networking Constructor //////////////////////////////////////////////////////
  402. // Handles any necessary setup of Network Module resources.
  403. Networking::Networking() { // Upon initialization,
  404. if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData)) { // startup the Winsock2.0 DLL.
  405. throw InitializationError( // If that fails then throw!
  406. "Networking::Networking() if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData))"
  407. );
  408. }
  409. }
  410. // Networking Destructor ///////////////////////////////////////////////////////
  411. // Handles any necessary cleanup of Network Module resources.
  412. Networking::~Networking() { // Upon shutdown,
  413. WSACleanup(); // shutdown the Winsock DLL.
  414. }
  415. //// Emd Windows specific code
  416. ////////////////////////////////////////////////////////////////////////////////
  417. #else
  418. ////////////////////////////////////////////////////////////////////////////////
  419. //// Begin GNU specific code
  420. // Error description handling for humans.
  421. std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno.
  422. Msg.append(" "); Msg.append(strerror(Errno));
  423. return Msg;
  424. };
  425. // Networking Constructor //////////////////////////////////////////////////////
  426. // Handles any necessary setup of Network Module resources.
  427. Networking::Networking() { // Upon initialization,
  428. // Nothing So Far... // nothing special required.
  429. }
  430. // Networking Destructor ///////////////////////////////////////////////////////
  431. // Handles any necessary cleanup of Network Module resources.
  432. Networking::~Networking() { // GNU sockets cleanup,
  433. // Nothing So Far... // nothing specail to required.
  434. }
  435. //// End GNU specific code
  436. ////////////////////////////////////////////////////////////////////////////////
  437. #endif
  438. ////////////////////////////////////////////////////////////////////////////////
  439. //// Platform Agnostic Stuff
  440. //// Useful Internal Bits & Pieces /////////////////////////////////////////////
  441. const int LowestOctetMask = 0x000000FF; // The bits to look at.
  442. const int OneOctetInBits = 8; // The bits to shift.
  443. void splitIP( // Split an IP into octets.
  444. unsigned long A, // The address in host format.
  445. int& a0, // Reference to the first octet.
  446. int& a1, // Reference to the second octet.
  447. int& a2, // Reference to the third octet.
  448. int& a3 // Reference to the forth octet.
  449. ){
  450. a3 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the lowest order octet & move.
  451. a2 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move.
  452. a1 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move.
  453. a0 = A & LowestOctetMask; // Get the highest octet. That's IT!
  454. }
  455. //// IP4Address methods ////////////////////////////////////////////////////////
  456. IP4Address::operator unsigned long int() const { // Assign to unsigned long int.
  457. return IP; // Return it.
  458. }
  459. IP4Address::operator std::string() const { // Assign to a string.
  460. char stringbfr[IPStringBufferSize]; // Grab a temporary buffer.
  461. memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space.
  462. int a0, a1, a2, a3; // Grab some integers.
  463. splitIP(IP, a0, a1, a2, a3); // Split the IP in the IP4Address.
  464. sprintf(stringbfr, "%d.%d.%d.%d", a0, a1, a2, a3); // Format the octets.
  465. return std::string(stringbfr); // Return a string.
  466. }
  467. //// SocketAddress methods /////////////////////////////////////////////////////
  468. // getAddress(str, len)
  469. const char* SocketAddress::getAddress(char* str) { // Get the IP address into a cstring.
  470. if(NULL == str) { // If the caller did not provide a
  471. str = IPStringBuffer; // buffer to use then we will use ours.
  472. }
  473. int a0, a1, a2, a3; // Grab a bunch of handy integers.
  474. getAddress(a0, a1, a2, a3); // Get the address as octets.
  475. sprintf(str, "%d.%d.%d.%d", a0, a1, a2, a3); // Format as dotted decimal notation.
  476. return str; // Return the output buffer.
  477. }
  478. // getAddress(int& a0, int& a1, int& a2, int& a3)
  479. void SocketAddress::getAddress(int& a0, int& a1, int& a2, int& a3) { // Get the IP address into 4 ints
  480. unsigned long A = getAddress(); // Get the address.
  481. splitIP(A, a0, a1, a2, a3); // Split it into octets.
  482. }
  483. //// TCPListener methods ///////////////////////////////////////////////////////
  484. TCPListener::TCPListener(unsigned short Port) { // Set up localhost on this Port.
  485. LocalAddress.setPort(Port); // Establish the port.
  486. LocalAddress.setAddress(LOCALHOST); // Set the address to LOCALHOST.
  487. MaxPending = DefaultMaxPending; // Use the default inbound queue size.
  488. ReuseAddress = true; // ReuseAddress on by default.
  489. OpenStage1Complete = false; // This stage of open() not yet done.
  490. OpenStage2Complete = false; // This stage of open() not yet done.
  491. // Create a socket...
  492. LastError = 0;
  493. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
  494. if(INVALID_SOCKET == Handle) { // If that operation failed then
  495. LastError = Network.getLastError(); // grab the error code and
  496. throw Networking::SocketCreationError( // throw.
  497. Network.DescriptiveError(
  498. "TCPListener::TCPListener().socket()", LastError));
  499. }
  500. }
  501. TCPListener::TCPListener(SocketAddress& WhereToBind) { // Set up specific "name" for listening.
  502. LocalAddress = WhereToBind; // Make my Local address as provided.
  503. MaxPending = DefaultMaxPending; // Use the default inbound queue size.
  504. ReuseAddress = true; // ReuseAddress on by default.
  505. OpenStage1Complete = false; // This stage of open() not yet done.
  506. OpenStage2Complete = false; // This stage of open() not yet done.
  507. // Create a socket...
  508. LastError = 0;
  509. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
  510. if(INVALID_SOCKET == Handle) { // If that operation failed then
  511. LastError = Network.getLastError(); // grab the error code and
  512. throw Networking::SocketCreationError( // throw.
  513. Network.DescriptiveError(
  514. "TCPListener::TCPListener().socket()", LastError));
  515. }
  516. }
  517. // open()
  518. void TCPListener::open() { // Open when ready.
  519. if(OpenSucceeded) return; // If open already, we're done.
  520. LastError = 0; // Clear the last error.
  521. bool SuccessFlag = true; // Start optimistically.
  522. // Set SO_REUSEADDR if turned on
  523. if(!OpenStage1Complete) { // Do this stage only once.
  524. int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag.
  525. int result = // Set SO_REUSEADDR before bind().
  526. setsockopt(
  527. Handle,
  528. SOL_SOCKET,
  529. SO_REUSEADDR,
  530. (char*) &ReuseAddress_Flag,
  531. sizeof(ReuseAddress_Flag));
  532. if(0 > result) { // If there was an error then
  533. SuccessFlag = false; // we did not succeed.
  534. LastError = Network.getLastError(); // Capture the error information and
  535. throw Networking::SocketSetSockOptError( // throw.
  536. Network.DescriptiveError(
  537. "TCPListener::open().setsockopt(SO_REUSEADDR)", LastError));
  538. }
  539. OpenStage1Complete = true; // Stage 1 complete now.
  540. } // End of open() stage 1
  541. // Next we bind it...
  542. if(!OpenStage2Complete) { // Do this stage only once.
  543. int result = // Bind our socket to the LocalAddress.
  544. bind(
  545. Handle,
  546. LocalAddress.getPtr_sockaddr(),
  547. LocalAddress.getAddressSize());
  548. if(0 > result) { // If there was an error then
  549. SuccessFlag = false; // we did not succeed.
  550. LastError = Network.getLastError(); // Capture the error information and
  551. throw Networking::SocketBindError( // throw.
  552. Network.DescriptiveError(
  553. "TCPListener::open().bind()", LastError));
  554. }
  555. OpenStage2Complete = true; // Stage 2 complete now.
  556. } // End of open() stage 2
  557. // Then we put it in a listening state...
  558. int result = listen(Handle, MaxPending); // Listen for up to MaxPending at once.
  559. if(0 > result) { // If an error occurred then
  560. SuccessFlag = false; // we did not succeed.
  561. LastError = Network.getLastError(); // Capture the error information and
  562. throw Networking::SocketListenError( // throw.
  563. Network.DescriptiveError(
  564. "TCPListener::open().listen()", LastError));
  565. }
  566. OpenSucceeded = SuccessFlag; // So, did we succeed?
  567. }
  568. // acceptClient()
  569. TCPClient* TCPListener::acceptClient() { // Accept a client connection.
  570. LastError = 0; // Clear the last error.
  571. socklen_t rsize = RemoteAddress.getAddressSize(); // Size as an int for accept().
  572. hSocket NewHandle = // Accept a new connection if available.
  573. accept(
  574. Handle, // use our handle, of course,...
  575. RemoteAddress.getPtr_sockaddr(), // and store the remote hosts
  576. &rsize); // address for us.
  577. if(INVALID_SOCKET == NewHandle) { // If there was an error then
  578. LastError = Network.getLastError(); // capture the error value.
  579. if(!Network.WouldBlock(LastError)) { // If it's not a EWOULDBLOCK error
  580. throw Networking::SocketAcceptError( // then we need to throw.
  581. Network.DescriptiveError(
  582. "TCPListener::acceptClient().accept()", LastError));
  583. } else { // EWOULDBLOCK errors are normal in
  584. return NULL; // non blocking mode so we return
  585. } // NULL when we see them.
  586. }
  587. // Set SO_NOSIGPIPE if needed
  588. if( // On some systems we may have to
  589. 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer
  590. 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead.
  591. ) {
  592. int TurnedOn = 1; // Prepare to turn this option on.
  593. int result = // Set SO_NOSIGPIPE.
  594. setsockopt(
  595. NewHandle,
  596. SOL_SOCKET,
  597. SO_NOSIGPIPE,
  598. (char*) &TurnedOn,
  599. sizeof(TurnedOn));
  600. if(0 > result) { // If there was an error then
  601. LastError = Network.getLastError(); // Capture the error information
  602. Network.closeSocket(NewHandle); // close the handle (avoid leaks)
  603. throw Networking::SocketSetSockOptError( // and throw a descriptive exception.
  604. Network.DescriptiveError(
  605. "TCPListener::acceptClient().setsockopt(SO_NOSIGPIPE)", LastError));
  606. }
  607. }
  608. // If things have gone well we can do what we came for.
  609. return new TCPClient(*this, NewHandle, RemoteAddress); // Create the new TCPClient object.
  610. }
  611. //// TCPClient methods /////////////////////////////////////////////////////////
  612. int TCPClient::transmit(const char* bfr, int size) { // How to send a buffer of data.
  613. if(0 == size) return 0; // Nothing to send, send nothing.
  614. if(0 == bfr) // Watch out for null buffers.
  615. throw Networking::SocketWriteError("TCPClient::transmit() NULL Bfr!");
  616. if(0 > size) // Watch out for bad sizes.
  617. throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!");
  618. LastError = 0; // No errors yet.
  619. int ByteCount = 0; // No bytes sent yet this pass.
  620. ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count.
  621. LastError = Network.getLastError(); // Grab any error code.
  622. bool AnErrorOccurred = (0 > ByteCount); // How to know if an error occurred.
  623. const int NoBytesSent = 0; // This is our "Would Block" result.
  624. if(AnErrorOccurred) { // If there was an error check it out.
  625. if(Network.WouldBlock(LastError)) { // If the error was "Would Block" then
  626. return NoBytesSent; // return no bytes sent (try again).
  627. } else { // If this was a different kind of error
  628. throw Networking::SocketWriteError( // then throw!
  629. Network.DescriptiveError(
  630. "TCPClient::transmit().send()", LastError));
  631. }
  632. }
  633. return ByteCount; // Usually: return the sent byte count.
  634. }
  635. int TCPClient::receive(char* bfr, int size) { // How to receive a buffer of data.
  636. if(ReadBufferIsEmpty()) { // If the read buffer is empty then
  637. fillReadBuffer(); // fill it first.
  638. } // Optimize our transfer to the smaller
  639. if(DataLength < size) { // of what we have or the size of the
  640. size = DataLength; // provided buffer. This way we ony check
  641. } // one value in our copy loop ;-)
  642. int RemainingDataLength = size; // Capture the length of data to xfer.
  643. while(0 < RemainingDataLength) { // While we have work to do
  644. *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
  645. bfr++; ReadPointer++; // move the pointers to the next byte,
  646. DataLength--; // update our ReadBuffers's DataLength,
  647. RemainingDataLength--; // and count down the bytes left to xfer.
  648. }
  649. return size; // When done, say how much we moved.
  650. }
  651. int TCPClient::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data.
  652. if(ReadBufferIsEmpty()) { // If the read buffer is empty then
  653. fillReadBuffer(); // fill it first.
  654. } // Optimize our transfer to the smaller
  655. if(DataLength < size) { // of what we have or the size of the
  656. size = DataLength; // provided buffer. This way we ony check
  657. } // one value in our copy loop ;-)
  658. int Count = 0; // Keep our byte count in scope.
  659. bool DelimiterNotReached = true; // Watching for our deliimiter.
  660. while((Count < size) && DelimiterNotReached) { // While there is work to do...
  661. *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
  662. DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character,
  663. bfr++; ReadPointer++; // move the pointers to the next byte,
  664. DataLength--; // update our ReadBuffers's DataLength,
  665. Count++; // and count up the bytes we have moved.
  666. }
  667. return Count; // When done, say how much we moved.
  668. }
  669. //// TCPHost methods ///////////////////////////////////////////////////////////
  670. // Constructors...
  671. TCPHost::TCPHost(unsigned short Port) { // Will connect to localhost on Port.
  672. RemoteAddress.setPort(Port); // Connect to Port on
  673. RemoteAddress.setAddress(LOCALHOST); // Localhost.
  674. ReadPointer = ReadBuffer; // Set the read position to zero.
  675. DataLength = 0; // There is no data yet.
  676. ReuseAddress = false; // ReuseAddress off by default.
  677. OpenStage1Complete = false; // Stage 1 of open() not done yet.
  678. // Create a socket to use.
  679. LastError = 0; // Clear our last error value.
  680. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
  681. if(0 > Handle) { // If that operation failed then
  682. LastError = Network.getLastError(); // grab the error code and
  683. throw Networking::SocketCreationError( // throw.
  684. Network.DescriptiveError(
  685. "TCPHost::TCPHost().socket()", LastError));
  686. }
  687. }
  688. TCPHost::TCPHost(SocketAddress& Remote) { // Will connect to Remote address/port.
  689. RemoteAddress = Remote; // Capture the provided address.
  690. ReadPointer = ReadBuffer; // Set the read position to zero.
  691. DataLength = 0; // There is no data yet.
  692. ReuseAddress = false; // ReuseAddress off by default.
  693. OpenStage1Complete = false; // Stage 1 of open() not done yet.
  694. // Create a socket to use.
  695. LastError = 0; // Clear our last error value.
  696. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket.
  697. if(0 > Handle) { // If that operation failed then
  698. LastError = Network.getLastError(); // grab the error code and
  699. throw Networking::SocketCreationError( // throw.
  700. Network.DescriptiveError(
  701. "TCPHost::TCPHost().socket()", LastError));
  702. }
  703. }
  704. // Methods...
  705. void TCPHost::open() { // We provide open().
  706. if(OpenSucceeded) return; // If open already, we're done.
  707. LastError = 0; // Clear our LastError value.
  708. bool SuccessFlag = true; // Begin optimistically.
  709. // Set Socket Options
  710. if(!OpenStage1Complete) { // If we haven't done this yet:
  711. // Set SO_REUSEADDR if turned on
  712. int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag.
  713. int result = // Set SO_REUSEADDR before bind().
  714. setsockopt(
  715. Handle,
  716. SOL_SOCKET,
  717. SO_REUSEADDR,
  718. (char*) &ReuseAddress_Flag,
  719. sizeof(ReuseAddress_Flag));
  720. if(0 > result) { // If there was an error then
  721. SuccessFlag = false; // we did not succeed.
  722. LastError = Network.getLastError(); // Capture the error information and
  723. throw Networking::SocketSetSockOptError( // throw.
  724. Network.DescriptiveError(
  725. "TCPHost::open().setsockopt(SO_REUSEADDR)", LastError));
  726. }
  727. // Set SO_NOSIGPIPE if needed
  728. if( // On some systems we may have to
  729. 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer
  730. 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead.
  731. ) {
  732. int TurnedOn = 1; // Prepare to turn this option on.
  733. int result = // Set SO_NOSIGPIPE.
  734. setsockopt(
  735. Handle,
  736. SOL_SOCKET,
  737. SO_NOSIGPIPE,
  738. (char*) &TurnedOn,
  739. sizeof(TurnedOn));
  740. if(0 > result) { // If there was an error then
  741. SuccessFlag = false; // we did not succeed.
  742. LastError = Network.getLastError(); // Capture the error information and
  743. throw Networking::SocketSetSockOptError( // throw.
  744. Network.DescriptiveError(
  745. "TCPHost::open().setsockopt(SO_NOSIGPIPE)", LastError));
  746. }
  747. }
  748. OpenStage1Complete = true; // Skip this section from now on.
  749. } // Done with stage 1.
  750. // Connect the socekt to the Host.
  751. int result = // Connect to the remote host
  752. connect( // using the socket we just
  753. Handle, // stored in Handle and
  754. RemoteAddress.getPtr_sockaddr(), // the Remote address.
  755. RemoteAddress.getAddressSize());
  756. if(0 > result) { // If there was an error then
  757. SuccessFlag = false; // we did not succeed.
  758. LastError = Network.getLastError(); // Record the error data.
  759. if(Network.IsConnected(LastError)) { // If we actually did succeed then
  760. SuccessFlag = true; // say so. (Silly Winsock!)
  761. } else // But if that's not the case check
  762. if( // to see if something bad happened -
  763. !Network.WouldBlock(LastError) && // not just would-block, or
  764. !Network.InProgress(LastError) // in progress...
  765. ) { // If it was something other than
  766. throw Networking::SocketConnectError( // WouldBlock or InProgress then
  767. Network.DescriptiveError( // throw!
  768. "TCPHost::open().connect()", LastError));
  769. } // If it was WouldBlock then it's
  770. } // considered to be ok.
  771. OpenSucceeded = SuccessFlag; // So, are we open now?
  772. }
  773. int TCPHost::transmit(const char* bfr, int size) { // How to send a buffer of data.
  774. LastError = 0; // No errors yet.
  775. if(0 == size) return 0; // Nothing to send, send nothing.
  776. if(0 == bfr) // Watch out for null buffers.
  777. throw Networking::SocketWriteError("TCPHost::transmit() NULL Bfr!");
  778. if(0 > size) // Watch out for bad sizes.
  779. throw Networking::SocketWriteError("TCPHost::transmit() 0 > size!");
  780. int ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count.
  781. if(0 > ByteCount) ByteCount = 0; // Mask error results as 0 bytes sent.
  782. if(size > ByteCount) { // If we didn't send it all check it out.
  783. LastError = Network.getLastError(); // Grab the error code.
  784. if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then
  785. return ByteCount; // it was a partial snd - return.
  786. } else { // If this was a different kind of error
  787. throw Networking::SocketWriteError( // then throw!
  788. Network.DescriptiveError(
  789. "TCPHost::transmit().send()", LastError));
  790. }
  791. }
  792. return ByteCount; // Ultimately return the byte count.
  793. }
  794. int TCPHost::receive(char* bfr, int size) { // How to receive a buffer of data.
  795. if(ReadBufferIsEmpty()) { // If the read buffer is empty then
  796. fillReadBuffer(); // fill it first.
  797. } // Optimize our transfer to the smaller
  798. if(DataLength < size) { // of what we have or the size of the
  799. size = DataLength; // provided buffer. This way we ony check
  800. } // one value in our copy loop ;-)
  801. int RemainingDataLength = size; // Capture the length of data to xfer.
  802. while(0 < RemainingDataLength) { // While we have work to do
  803. *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
  804. bfr++; ReadPointer++; // move the pointers to the next byte,
  805. DataLength--; // update our ReadBuffers's DataLength,
  806. RemainingDataLength--; // and count down the bytes left to xfer.
  807. }
  808. return size; // When done, say how much we moved.
  809. }
  810. int TCPHost::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data.
  811. if(ReadBufferIsEmpty()) { // If the read buffer is empty then
  812. fillReadBuffer(); // fill it first.
  813. } // Optimize our transfer to the smaller
  814. if(DataLength < size) { // of what we have or the size of the
  815. size = DataLength; // provided buffer. This way we ony check
  816. } // one value in our copy loop ;-)
  817. int Count = 0; // Keep our byte count in scope.
  818. bool DelimiterNotReached = true; // Watching for our deliimiter.
  819. while((Count < size) && DelimiterNotReached) { // While there is work to do...
  820. *bfr = *ReadPointer; // copy each byte from our ReadBuffer,
  821. DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character,
  822. bfr++; ReadPointer++; // move the pointers to the next byte,
  823. DataLength--; // update our ReadBuffers's DataLength,
  824. Count++; // and count up the bytes we have moved.
  825. }
  826. return Count; // When done, say how much we moved.
  827. }
  828. // End Platform Agnostic Stuff
  829. ////////////////////////////////////////////////////////////////////////////////
  830. } // End namespace codedweller