2
0

tcpcon.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "tcpcon.h"
  19. #include <time.h>
  20. #define DEFAULT_TCP_DELAY 15
  21. TCPCon::TCPCon(SOCKET sock) :
  22. BufferedWrites_(false),
  23. TCPMgrPtr_(NULL)
  24. {
  25. Socket_=sock;
  26. isConnected(); // This will set the internal connect status
  27. setInputDelay(DEFAULT_TCP_DELAY);
  28. setOutputDelay(DEFAULT_TCP_DELAY);
  29. }
  30. TCPCon::~TCPCon()
  31. {
  32. close(); // will try and flush the write queue
  33. ReadQueue_.clear();
  34. WriteQueue_.clear();
  35. if ((BufferedWrites_) && (TCPMgrPtr_))
  36. TCPMgrPtr_->setBufferedWrites(this, FALSE);
  37. }
  38. SOCKET TCPCon::getFD(void)
  39. { return(Socket_); }
  40. void TCPCon::close(void)
  41. {
  42. if (BufferedWrites_)
  43. {
  44. while(WriteQueue_.length())
  45. pumpWrites();
  46. }
  47. ::closesocket(Socket_);
  48. State_=TCPMgr::CLOSED;
  49. }
  50. //
  51. // Write data
  52. //
  53. // Returns 'n' bytes written, 0 if closed, or -1 for error.
  54. //
  55. sint32 TCPCon::write(IN uint8 *msg,uint32 len, sint32 wait_secs)
  56. {
  57. if (State_==TCPMgr::CLOSED)
  58. return(0);
  59. if (BufferedWrites_)
  60. {
  61. WriteQueue_.addMany(msg, WriteQueue_.length(), len); // add to tail
  62. pumpWrites(); // send what I can
  63. return(len);
  64. }
  65. else
  66. {
  67. return (normalWrite(msg, len, wait_secs));
  68. }
  69. }
  70. //
  71. // set buffered status
  72. //
  73. void TCPCon::setBufferedWrites(TCPMgr *mgrptr, bit8 enabled)
  74. {
  75. if (enabled)
  76. BufferedWrites_=TRUE;
  77. else
  78. {
  79. while(WriteQueue_.length())
  80. pumpWrites();
  81. BufferedWrites_=FALSE;
  82. }
  83. TCPMgrPtr_=mgrptr;
  84. }
  85. //
  86. // Try and send queued data (PRIVATE)
  87. //
  88. void TCPCon::pumpWrites(void)
  89. {
  90. if (State_==TCPMgr::CLOSED)
  91. WriteQueue_.clear();
  92. if (WriteQueue_.length()==0)
  93. return;
  94. int sendlen;
  95. int retval;
  96. uint8 *bufptr=NULL;
  97. while(1)
  98. {
  99. sendlen=WriteQueue_.length();
  100. if (sendlen == 0)
  101. break;
  102. WriteQueue_.getPointer(&bufptr,0); // pointer to first byte
  103. retval=normalWrite((uint8 *)bufptr, sendlen, 0);
  104. if (retval <= 0)
  105. break;
  106. WriteQueue_.removeMany(NULL, 0, retval); // successful send
  107. }
  108. return;
  109. }
  110. //
  111. // Non-buffered write (PRIVATE METHOD)
  112. //
  113. // Returns 'n' bytes written, 0 if closed, or -1 for error.
  114. //
  115. sint32 TCPCon::normalWrite(IN uint8 *msg,uint32 len, sint32 wait_secs)
  116. {
  117. if (State_==TCPMgr::CLOSED)
  118. return(0);
  119. if (wait_secs < 0)
  120. wait_secs=OutputDelay_;
  121. sint32 retval=0;
  122. sint32 sendCount=0;
  123. time_t start=time(NULL);
  124. TCPMgr::STATUS status;
  125. while(1)
  126. {
  127. retval=send(Socket_,(const char *)(msg+sendCount),(len-sendCount),0);
  128. if ((retval > 0) && (retval+sendCount)==int(len))
  129. break;
  130. if (retval==SOCKET_ERROR)
  131. {
  132. status=TCPMgr::getStatus();
  133. if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) && (status != TCPMgr::INPROGRESS))
  134. {
  135. if (sendCount)
  136. return(sendCount);
  137. else
  138. return(-1);
  139. }
  140. }
  141. else if (retval > 0)
  142. sendCount+=retval;
  143. sint32 remaining_wait=wait_secs - (time(NULL)-start);
  144. if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,FALSE) > 0))
  145. continue; // I can write now....
  146. if (remaining_wait <= 0)
  147. break;
  148. }
  149. return(retval);
  150. }
  151. //
  152. // Read data
  153. //
  154. // Returns 'n' bytes read, 0 for close, or -1 for error.
  155. // This may return less than we asked for
  156. //
  157. sint32 TCPCon::read(OUT uint8 *msg,uint32 maxlen, sint32 wait_secs)
  158. {
  159. sint32 retval=0;
  160. sint32 recvCount=0;
  161. time_t start=time(NULL);
  162. char readBuffer[257];
  163. if (wait_secs < 0)
  164. wait_secs=InputDelay_;
  165. if (State_==TCPMgr::CLOSED)
  166. return(0);
  167. TCPMgr::STATUS status;
  168. while(1)
  169. {
  170. // Do we even nead to read from the net?
  171. if (ReadQueue_.length() >= int(maxlen))
  172. {
  173. ReadQueue_.removeMany(msg, 0, maxlen);
  174. return(maxlen);
  175. }
  176. do
  177. {
  178. retval=recv(Socket_,readBuffer, 256,0);
  179. if (retval > 0) // add to the tail of the list
  180. {
  181. readBuffer[retval]=0;
  182. DBGMSG("RECV: "<<readBuffer);
  183. // Add to tail
  184. ReadQueue_.addMany((uint8 *)readBuffer, ReadQueue_.length(), retval);
  185. }
  186. } while ((retval > 0)&&(ReadQueue_.length() < int(maxlen)));
  187. if (ReadQueue_.length()) // OK, we'll take what we've got
  188. {
  189. uint8 *cptr;
  190. ReadQueue_.getPointer(&cptr,0);
  191. /*******
  192. fprintf(stderr,"ReadQueue(%d): '",ReadQueue_.length());
  193. for (int i=0; i<ReadQueue_.length(); i++)
  194. fprintf(stderr,"%c",cptr[i]);
  195. fprintf(stderr,"'\n");
  196. ********/
  197. int retcount=MIN(ReadQueue_.length(), int(maxlen));
  198. ReadQueue_.removeMany(msg, 0, retcount);
  199. return(retcount);
  200. }
  201. else if (retval==0)
  202. {
  203. close();
  204. return(0);
  205. }
  206. else if (retval==SOCKET_ERROR)
  207. {
  208. status=TCPMgr::getStatus();
  209. if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) &&
  210. (status != TCPMgr::INPROGRESS))
  211. return(-1);
  212. }
  213. sint32 remaining_wait=wait_secs - (time(NULL)-start);
  214. if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,TRUE) > 0))
  215. continue; // I can read now....
  216. if (remaining_wait <= 0)
  217. break;
  218. }
  219. return(retval);
  220. }
  221. // Push data back onto the read queue
  222. bit8 TCPCon::unread(uint8 *data, int length)
  223. {
  224. ReadQueue_.addMany(data, 0, length);
  225. return(TRUE);
  226. }
  227. // Returns 0 on failure
  228. // Returns IP in host byte order!
  229. bit8 TCPCon::getRemoteAddr(uint32 *ip, uint16 *port)
  230. {
  231. struct sockaddr_in sin;
  232. int sinSize=sizeof(sin);
  233. if(getpeername(Socket_,(sockaddr *)&sin,&sinSize)==0)
  234. {
  235. if (ip)
  236. *ip=ntohl(sin.sin_addr.s_addr);
  237. if (port)
  238. *port=ntohs(sin.sin_port);
  239. return(TRUE);
  240. }
  241. return(FALSE);
  242. }
  243. //
  244. // only use for strings up to 4096 chars!
  245. //
  246. sint32 TCPCon::printf(const char *format, ...)
  247. {
  248. va_list arg;
  249. char string[4097];
  250. sint32 retval;
  251. va_start(arg,format);
  252. vsprintf(string,format,arg);
  253. va_end(arg);
  254. string[4096]=0;
  255. retval=write((IN uint8 *)string,strlen(string), OutputDelay_);
  256. return(retval);
  257. }
  258. bit8 TCPCon::isConnected(void)
  259. {
  260. uint32 remoteIp;
  261. uint16 remotePort;
  262. if (getRemoteAddr(&remoteIp,&remotePort)==TRUE)
  263. {
  264. State_=TCPMgr::CONNECTED;
  265. return(TRUE);
  266. }
  267. else
  268. {
  269. State_=TCPMgr::CLOSED;
  270. return(FALSE);
  271. }
  272. }
  273. /*********
  274. // For the OutputDevice interface
  275. int TCPCon::print(IN char *str, int len)
  276. {
  277. return(write((IN uint8 *)str,len,0));
  278. }
  279. ********/