home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / OWLSRC.PAK / WSKSOCKD.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  18.7 KB  |  523 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.13  $
  6. //
  7. // Winsock for OWL subsystem.
  8. // Based on work by Paul Pedriana, 70541.3223@compuserve.com
  9. //----------------------------------------------------------------------------
  10. #include <owl/pch.h>
  11. #if !defined(OWL_WINSOCK_H)
  12. # include <owl/winsock.h>
  13. #endif
  14.  
  15. OWL_DIAGINFO;
  16.  
  17. //
  18. // Actually 512 is the absolute guaranteed minimum value.  The WSAData
  19. // structure has the actual value (>=512).
  20. //
  21. int TDatagramSocket::MaxPacketSendSize = 512;
  22.  
  23. //
  24. // Does nothing. Relies on TSocket to do all the work.
  25. //
  26. TDatagramSocket::TDatagramSocket()
  27. :
  28.   TSocket()
  29. {
  30. }
  31.  
  32. //
  33. // Does nothing. Relies on TSocket to do all the work.
  34. //
  35. TDatagramSocket::TDatagramSocket(SOCKET& src)
  36. :
  37.   TSocket(src)
  38. {
  39. }
  40.  
  41. //
  42. // Does nothing. Relies on TSocket to do all the work.
  43. //
  44. TDatagramSocket::TDatagramSocket(TSocketAddress& socketAddress, int addressFormat,
  45.                                  int type, int protocol)
  46. :
  47.   TSocket(socketAddress, addressFormat, type, protocol)
  48. {
  49. }
  50.  
  51. //
  52. // Reads the Chars into the chData buffer, and removes the data from the queue.
  53. // chData is a pointer to a destination buffer for the data.
  54. // nCharsToRead should be set to the maximum desired read size, which needs to be
  55. //  equal or less to the size of chData.
  56. // The sAddress parameter will get filled with the address of the sender.
  57. // Returns WINSOCK_ERROR if there was an error, WINSOCK_NOERROR otherwise.
  58. // This Read will get the data from the next buffered packet and delete the packet from
  59. //  memory when finished.  Thus if this function is called with a 'nCharsToRead' that
  60. //  is less than the size of the next packet, then only part of the packet will be
  61. //  read and the rest will be lost.
  62. // Upon return, nCharsToRead will be set to the actual number that were read.  If
  63. //  nCharsToRead returns as 0, then no data was read.  If the function return value
  64. //  is WINSOCK_ERROR, then there was a Winsock error, otherwise, the call was
  65. //  successful, even though no data may have been read.
  66. //
  67. int TDatagramSocket::Read(char* data, int& charsToRead, TSocketAddress& address)
  68. {
  69.   // Simply call recvfrom() and return if some kind of error is encountered.
  70.   //
  71.   int addressSize = sizeof(sockaddr);
  72.   int charsReceived = TWinSockDll::recvfrom(Handle, data, charsToRead, 0,
  73.                                               &address, &addressSize);
  74.  
  75.   if (charsReceived == SOCKET_ERROR) {
  76.     // It is entirely possible to get a blocking error here.  For example, a
  77.     //  call to recv() could have been made right after the message was
  78.     //  posted by the driver, but before this notification function got called.
  79.     //
  80.     charsToRead = 0;  // This will be available for the caller to examine.
  81.     LastError = TWinSockDll::WSAGetLastError();
  82.     if (LastError == WSAEWOULDBLOCK)
  83.       return WINSOCK_NOERROR;  // This is not considered an "error" under Winsock.
  84.     // Some other error occurred, return from this function.
  85.     //
  86.     return WINSOCK_ERROR;
  87.   }
  88.   charsToRead = charsReceived;
  89.   return WINSOCK_NOERROR;
  90. }
  91.  
  92. //
  93. // Puts the data in the queue and attempts to actually write out the first item in the queue.
  94. // At the end of the function, an attempt to write out the queue is made.  If it fails, well, the
  95. //  data will get sent later, after the system has notified us it is ready.
  96. // Returns WINSOCK_ERROR or WINSOCK_NOERROR.
  97. //
  98. int TDatagramSocket::Write(char* data, int& charsToWrite, TSocketAddress& outSocketAddress,
  99.                            bool /*becomeOwnerOfData*/, bool /*copyData*/)
  100. {
  101.   // Note that bBecomeOwnerOfdata and bCopyData are ignored if we are using DataQueues.
  102.   // Note that thus function may block here if blocking is enabled.
  103.   //
  104.   int charsSent = TWinSockDll::sendto(Handle, data, charsToWrite, 0,
  105.                                    &outSocketAddress, sizeof(sockaddr));
  106.   if (charsSent == SOCKET_ERROR) {
  107.     //It is entirely possible to get a blocking error here.  For example, a call to
  108.     //  send() could have been made right after the message was posted by the driver,
  109.     //  but before this notification function got called.
  110.     //
  111.     charsToWrite = 0;
  112.     LastError = TWinSockDll::WSAGetLastError();
  113.     if (LastError == WSAEWOULDBLOCK)
  114.       return WINSOCK_NOERROR;
  115.     return WINSOCK_ERROR;
  116.   }
  117.   charsToWrite = charsSent;
  118.   return WINSOCK_NOERROR;
  119. }
  120.  
  121. //
  122. // Simply calls the other Write() function with our latest address.
  123. // Arguments and return values are the same.
  124. //
  125. int TDatagramSocket::Write(char* data, int& charsToWrite, bool becomeOwnerOfData, bool copyData)
  126. {
  127.   return Write(data, charsToWrite, PeerSocketAddress, becomeOwnerOfData, copyData);
  128. }
  129.  
  130. //
  131. // This function gets called whenever the socket gets a read notification.  This means
  132. //  that data on the port is ready to be read.
  133. //
  134. // This function doesn't do much with the nError parameter.  It simply doesn't do the
  135. //  read if there is an error value.  According to Winsock documentation, this
  136. //  error may be "any error in winsock.h".
  137. //
  138. TDatagramSocket::DoReadNotification(const SOCKET& /*socket*/, int error)
  139. {
  140.   if (error) {
  141.     LastError = error; // TWinSockDll::WSAGetLastError();
  142.     return WINSOCK_ERROR;
  143.   }
  144.  
  145.   // This function dan't do anything on its own without data queues.  Without
  146.   //  DataQueues, you can do two things:  1) subclass TDatagramSocket and write
  147.   //  your own DoReadNotification() or 2) Have the socket redirect the FD_READ
  148.   //  notification to the window of your choice with Socket::SetNotificationSet()
  149.   //  and SetNotificationWindow().
  150.   //
  151.   return WINSOCK_NOERROR;
  152. }
  153.  
  154. //
  155. // This function gets called whenever the socket gets a write notification.
  156. //
  157. TDatagramSocket::DoWriteNotification(const SOCKET& /*s*/, int error)
  158. {
  159.   if (error) {
  160.     LastError = error; // TWinSockDll::WSAGetLastError();
  161.     return WINSOCK_ERROR;
  162.   }
  163.  
  164.   // This function dan't do anything on its own without data queues.  Without
  165.   //  DataQueues, you can do two things:  1) subclass TDatagramSocket and write
  166.   //  your own DoWriteNotification() or 2) Have the socket redirect the FD_WRITE
  167.   //  notification to the window of your choice with Socket::SetNotificationSet()
  168.   //  and SetNotificationWindow().
  169.   //
  170.   return WINSOCK_NOERROR;
  171. }
  172.  
  173. //----------------------------------------------------------------------------
  174.  
  175. //
  176. // Calls TSocket constructor and initializes the state of the connection
  177. // to not connected.
  178. //
  179. TStreamSocket::TStreamSocket()
  180. :
  181.   TSocket(),
  182.   ConnectStatus(NotConnected)
  183. {
  184. }
  185.  
  186. //
  187. // Alias constructor.
  188. //
  189. TStreamSocket::TStreamSocket(SOCKET& src)
  190. :
  191.   TSocket(src)
  192. {
  193.   //  what the ConnectStatus is, given just a SOCKET descriptor.
  194.   // Needs to be set somehow:
  195.   // nConnectStatus = ???;
  196. }
  197.  
  198. //
  199. // Constructor for a protocol defined by an int.
  200. //
  201. TStreamSocket::TStreamSocket(TSocketAddress& socketAddress, int addressFormat, int type, int protocol)
  202. :
  203.   TSocket(socketAddress, addressFormat, type, protocol),
  204.   ConnectStatus(NotConnected)
  205. {
  206. }
  207.  
  208. //
  209. // Copies the socket connection information.
  210. //
  211. TStreamSocket& TStreamSocket::operator =(TStreamSocket& src)  
  212. {
  213.   TSocket::operator =(src);
  214.   return *this;
  215. }
  216.  
  217. //
  218. // This reads from the already received and queued data.  This data has already
  219. //  been received from the socket driver.
  220. //
  221. int TStreamSocket::Read(char* data, int& charsToRead)
  222. {
  223.   // Try to receive the characters.
  224.   //
  225.   int charsReceived = TWinSockDll::recv(Handle, data, charsToRead, 0); 
  226.   if (charsReceived == SOCKET_ERROR) {
  227.     // It is entirely possible to get a blocking error here.  For example, a call to
  228.     //  recv() could have been made right after the message was posted by the driver,
  229.     //  but before this notification function got called.
  230.     //
  231.     charsToRead = 0; // This will be available for the caller to examine.
  232.     LastError = TWinSockDll::WSAGetLastError();
  233.     if (LastError == WSAEWOULDBLOCK)
  234.       return WINSOCK_NOERROR;
  235.     // Some other error occurred.
  236.     //
  237.     return WINSOCK_ERROR;
  238.   }
  239.   charsToRead = charsReceived;
  240.   return WINSOCK_NOERROR;
  241. }
  242.  
  243. //
  244. // Write the buffer into the stream.
  245. //
  246. int TStreamSocket::Write(char* data, int& charsToWrite, int flags,
  247.                 bool /*becomeOwnerOfData*/, bool /*copyData*/)
  248. {
  249.   int charsSent = TWinSockDll::send(Handle, data, charsToWrite, flags);
  250.   if (charsSent == SOCKET_ERROR) {
  251.     // It is entirely possible to get a blocking error here.  For example, a call to
  252.     //  send() could have been made right after the message was posted by the driver,
  253.     //  but before this notification function got called.
  254.     //
  255.     charsToWrite = 0;
  256.     LastError = TWinSockDll::WSAGetLastError();
  257.     if (LastError == WSAEWOULDBLOCK) {
  258.       return WINSOCK_NOERROR;
  259.     }
  260.     return WINSOCK_ERROR;
  261.   }
  262.   charsToWrite = charsSent;
  263.   return WINSOCK_NOERROR;
  264. }
  265.  
  266. //
  267. // Works just like the Read() function but it works on the OOB queue.
  268. //
  269. int TStreamSocket::ReadOOB(char* data, int& charsToRead)
  270. {
  271.   // Try to receive the characters.
  272.   //
  273.   int charsReceived = TWinSockDll::recv(Handle, data, charsToRead, MSG_OOB);
  274.   if (charsReceived == SOCKET_ERROR) {
  275.     // It is entirely possible to get a blocking error here.  For example, a call to
  276.     //  recv() could have been made right after the message was posted by the driver,
  277.     //  but before this notification function got called.
  278.     //
  279.     charsToRead = 0;
  280.     LastError = TWinSockDll::WSAGetLastError();
  281.     if (LastError == WSAEWOULDBLOCK)
  282.       return WINSOCK_NOERROR;
  283.     // Some other error occurred.
  284.     return WINSOCK_ERROR;
  285.   }
  286.   charsToRead = charsReceived;
  287.   return WINSOCK_NOERROR;
  288. }
  289.  
  290. //
  291. // Perform write operation using out-of-band data (i.e. via a logically
  292. // independent tranmission channel between the connected sockets).
  293. // NOTE: For 'non urgent data', you may used TStreamSocket::Write() instead.
  294. //
  295. int TStreamSocket::WriteOOB(char* data, int& charsToWrite, int flags,
  296.                             bool /*becomeOwnerOfData*/, bool /*copyData*/)
  297. {
  298.   flags |= MSG_OOB;
  299.   int charsSent = TWinSockDll::send(Handle, data, charsToWrite, flags);
  300.   if (charsSent == SOCKET_ERROR) {
  301.     // It is entirely possible to get a blocking error here.  For example, a call to
  302.     //  send() could have been made right after the message was posted by the driver,
  303.     //  but before this notification function got called.
  304.     //
  305.     charsToWrite = 0;
  306.     LastError = TWinSockDll::WSAGetLastError();
  307.     if (LastError == WSAEWOULDBLOCK) {
  308.       return WINSOCK_NOERROR;
  309.     }
  310.     return WINSOCK_ERROR;
  311.   }
  312.   charsToWrite = charsSent;
  313.   return WINSOCK_NOERROR;
  314. }
  315.  
  316. //
  317. // This function puts this socket into a passive listening mode.
  318. //
  319. TStreamSocket::Listen(int maxQueuedConnections)
  320. {
  321.   if (TWinSockDll::listen(Handle, maxQueuedConnections)) {
  322.     LastError = TWinSockDll::WSAGetLastError();
  323.     return WINSOCK_ERROR;
  324.   }
  325.   ConnectStatus = Listening;
  326.   return WINSOCK_NOERROR;
  327. }
  328.  
  329. //
  330. // This function sets myPeerSocketAddress to sAddressToConnectTo then calls Connect().
  331. // See Connect() for more details on it.
  332. //
  333. TStreamSocket::Connect(TSocketAddress& addressToConnectTo)
  334. {
  335.   SetPeerSocketAddress(addressToConnectTo);
  336.   return Connect();
  337. }
  338.  
  339. //
  340. // This function uses 'myPeerSocketAddress'; it needs to be set before callign this function.
  341. // The connection attempt (and this function) should return right away, without blocking.
  342. // When we are actually connected, we get a notification from the driver at our
  343. //  'DoConnectNotification()' function.  Upon receiving that notification, the nConnectStatus
  344. //  will get set to 'nConnected'.
  345. //
  346. // Technically, it is true that a datagram socket can call connect; doing this sets the default address
  347. //  for future send()/recv() calls tha the datagram socket might use.
  348. //  We put it in the TStreamSocket class for simplicity, and because our TDatagramSocket class
  349. //  already supports its own default address system.
  350. //
  351. TStreamSocket::Connect()
  352. {
  353.   if (TWinSockDll::connect(Handle, &PeerSocketAddress, sizeof(sockaddr))) {
  354.     LastError = TWinSockDll::WSAGetLastError();
  355.     return WINSOCK_ERROR;
  356.   }
  357.   // The following code assumes that the socket is in non-blocking mode.  If the socket was in
  358.   //  blocking mode, then nConnectStatus would be nConnected.  Thus, this code is slightly
  359.   //  bugged and can be patched with a call to select() to determine whether the socket is
  360.   //  really connected or not.  Looked at another way, a blocking socket can be said to be
  361.   //  connected if nConnectStatus is either nConnected or nConnectPending.
  362.   //
  363.   ConnectStatus = ConnectPending;
  364.   return WINSOCK_NOERROR;
  365. }
  366.  
  367. //
  368. // This function will try to accept a connection with the first connecting peer that
  369. //  is waiting in the queue.  If successful, the TStreamSocket& 'socket' will have a
  370. //  valid and connected socket, a proper status of nConnected, and the correct
  371. //  peer socket address.
  372. //
  373. // The caller will usually call this function in response to an Accept notification.
  374. //  The caller merely needs to create a new TStreamSocket and pass it to this
  375. //  function. The default constructor for TStreamSocket can be used, as this function
  376. //  fixes up the missing parts.
  377. //
  378. TStreamSocket::Accept(TStreamSocket& socket)
  379. {
  380.   if (Accept(socket.Handle, socket.PeerSocketAddress) == WINSOCK_NOERROR) {
  381.     socket.ConnectStatus = Connected;
  382.     socket.SetSocketStyle(Family, Type, Protocol); //Copy our Family, etc. just to make sure.
  383.     return WINSOCK_NOERROR;
  384.   }
  385.   return WINSOCK_ERROR;
  386. }
  387.  
  388. //
  389. // This function will try to accept a connection with the first connecting peer that is
  390. //  waiting in the queue.  If successful, the 'socket' reference argument will be set to
  391. //  a new connected socket.  The sAddress reference argument will get set to the address
  392. //  of the connecting peer.  The caller of this function may immediately use the
  393. //  new socket with data sends, etc.  Note that the caller may want to flag the socket
  394. //  as connected.  If the 'socket' belongs to a 'StreanSocket', then you can set its
  395. //  nConnectionStatus as nConnected.
  396. //
  397. // The return value is either WINSOCK_ERROR or WINSOCK_NOERROR.  If there is an error,
  398. //  then nLastError will be set with the appropriate error.
  399. //
  400. // Note that it is possible that this call could be made when there are no pending
  401. //  socket connecions in the queue.  If this is the case, the call will block
  402. //  if the socket is marked as blocking, and will return WINSOCK_ERROR with
  403. //  WSAEWOULDBLOCK if the socket is marked as non-blocking.
  404. //
  405. // This function is usually called in response to an accept notification.  A socket is
  406. //  set up as a stream socket and listen() is called.  When a connection is ready, the
  407. //  driver notifies the listening socket with a DoAcceptNotification() call.  See the
  408. //  DoAcceptNotification() call for what to do with it.  You will want to call this Accept()
  409. //  function as a result.
  410. //
  411. TStreamSocket::Accept(SOCKET& socket, sockaddr& address)
  412. {
  413.   int addressLength = sizeof(sockaddr);
  414.   socket = TWinSockDll::accept(Handle, &address, &addressLength);
  415.  
  416.   if (socket == INVALID_SOCKET) {
  417.     LastError = TWinSockDll::WSAGetLastError();
  418.     return WINSOCK_ERROR;
  419.   }
  420.   return WINSOCK_NOERROR;
  421. }
  422.  
  423. //
  424. // This function gets called whenever the socket gets a read notification.  This means
  425. //  that data on the port is ready to be read.
  426. //
  427. // This function doesn't do much with the nError parameter.  It simply doesn't do the
  428. //  read if there is an error value.  According to Winsock documentation, this
  429. //  error may be "any error in winsock.h".
  430. //
  431. TStreamSocket::DoReadNotification(const SOCKET& /*socket*/, int error)
  432. {
  433.   if (error) {
  434.     LastError = error; // TWinSockDll::WSAGetLastError();
  435.     return WINSOCK_ERROR;
  436.   }
  437.   return WINSOCK_NOERROR;
  438. }
  439.  
  440. //
  441. // This function gets called whenever the socket gets a write notification.
  442. // This means that data on the port is ready to be written.
  443. //
  444. TStreamSocket::DoWriteNotification(const SOCKET& /*s*/, int error)
  445. {
  446.   if (error) {
  447.     LastError = error;  
  448.     return WINSOCK_ERROR;
  449.   }
  450.  
  451.   // This function dan't do anything on its own without data queues.  Without
  452.   //  DataQueues, you can do two things:  1) subclass TStreamSocket and write
  453.   //  your own DoWriteNotification() or 2) Have the socket redirect the FD_WRITE
  454.   //  notification to the window of your choice with Socket::SetNotificationSet()
  455.   //  and SetNotificationWindow().
  456.   //
  457.   return WINSOCK_NOERROR;
  458. }
  459.  
  460. //
  461. // We get this notification when OOB data is ready to be received on the socket port.
  462. //
  463. TStreamSocket::DoOOBNotification(const SOCKET& /*s*/, int error)
  464. {
  465.   if (error) {
  466.     LastError = error; // TWinSockDll::WSAGetLastError();
  467.     return WINSOCK_ERROR;
  468.   }
  469.  
  470.   // This function dan't do anything on its own without data queues.  Without
  471.   //  DataQueues, you can do two things:  1) subclass TStreamSocket and write
  472.   //  your own DoOOBNotification() or 2) Have the socket redirect the FD_OOB
  473.   //  notification to the window of your choice with Socket::SetNotificationSet()
  474.   //  and SetNotificationWindow().
  475.   //
  476.   return WINSOCK_NOERROR;
  477. }
  478.  
  479. //
  480. // We get this notification when a client socket on the network is attempting to connect to us.
  481. //Code needs to be written to intercept this notification.
  482. //
  483. TStreamSocket::DoAcceptNotification(const SOCKET& /*s*/, int /*error*/)
  484. {
  485.   return 0; // We don't do anything.  We let the pending acceptance sit there.
  486. }
  487.  
  488. //
  489. // This means that the connection that we attempted with a server on the network has completed.
  490. // This function gets called sometime after this object makes a connect() attempt.  If the
  491. //  connect attempt was non-blocking, then a notification is posted and we end up here.
  492. // When this function gets called, we our nConnectStatus should be 'nConnecting'.
  493. //
  494. TStreamSocket::DoConnectNotification(const SOCKET& /*s*/, int error)
  495. {
  496.   if (error) {
  497.     // There was an error, and we cannot connect; this may due to a number of reasons.
  498.     // We turn off our 'nConnectStatus' to 'nNotConnected'.  This is becuase our attempt
  499.     //  to connect failed.
  500.     //
  501.     ConnectStatus = NotConnected;
  502.     LastError = error;   
  503.     return WINSOCK_ERROR;
  504.   }
  505.   ConnectStatus = Connected;
  506.   return WINSOCK_NOERROR;
  507. }
  508.  
  509. //
  510. // This notification gets called when the socket has been closed.
  511. // We mark the socket as not connected, so that the user may know about it.
  512. // It is important that we read any data that may be waiting in the queue before
  513. // changing the status of the connection and doing any notification.
  514. //
  515. TStreamSocket::DoCloseNotification(const SOCKET& /*s*/, int error)
  516. {
  517.   if (error) {
  518.     LastError = error;  
  519.     return WINSOCK_ERROR;
  520.   }
  521.   return WINSOCK_NOERROR;
  522. }
  523.