//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Winsock for OWL subsystem.
/// Based on work by Paul Pedriana, 70541.3223@compuserve.com
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/defs.h>
#include <owl/winsock.h>
namespace owl {
OWL_DIAGINFO;
//
/// Actually 512 is the absolute guaranteed minimum value. The WSAData
/// structure has the actual value (>=512).
//
int TDatagramSocket::MaxPacketSendSize = 512;
//
/// This function does nothing. It relies on TSocket to do all the work.
//
TDatagramSocket::TDatagramSocket()
:
TSocket()
{
}
//
/// This function does nothing. It relies on TSocket to do all the work.
//
TDatagramSocket::TDatagramSocket(SOCKET& src)
:
TSocket(src)
{
}
//
/// This function does nothing. It relies on TSocket to do all the work.
//
TDatagramSocket::TDatagramSocket(TSocketAddress& socketAddress, int addressFormat,
int type, int protocol)
:
TSocket(socketAddress, addressFormat, type, protocol)
{
}
//
/// This function reads the chars into the chData buffer, and removes the data from
/// the queue. chData is a pointer to a destination buffer for the data.
/// nCharsToRead should be set to the maximum desired read size, which needs to be
/// equal or less to the size of chData.
/// The sAddress parameter is filled with the address of the sender. It returns
/// WINSOCK_ERROR if there was an error, WINSOCK_NOERROR otherwise. This Read will
/// get the data from the next buffered packet and delete the packet from memory
/// when finished. Thus if this function is called with a nCharsToRead that is
/// smaller than the next packet, only part of the packet will be read and the rest
/// will be lost.
/// Upon return, nCharsToRead is set to the actual number of characters read. If
/// nCharsToRead returns 0, no data was read. If the function return value is
/// WINSOCK_ERROR, there was a Windows Sockets error. Otherwise, the call was
/// successful, even though no data may have been read.
//
int TDatagramSocket::Read(char* data, int& charsToRead, TSocketAddress& address)
{
// Simply call recvfrom() and return if some kind of error is encountered.
//
int addressSize = sizeof(sockaddr);
///PP Need to change this to allow flags.
int charsReceived = TWinSockDll::recvfrom(Handle, data, charsToRead, 0,
&address, &addressSize);
if (charsReceived == SOCKET_ERROR) {
// It is entirely possible to get a blocking error here. For example, a
// call to recv() could have been made right after the message was
// posted by the driver, but before this notification function got called.
//
charsToRead = 0; // This will be available for the caller to examine.
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK)
return WINSOCK_NOERROR; // This is not considered an "error" under Winsock.
// Some other error occurred, return from this function.
//
return WINSOCK_ERROR;
}
charsToRead = charsReceived;
return WINSOCK_NOERROR;
}
//
/// This function puts the data in the queue and attempts to write the first item in
/// the queue. At the end of the function, an attempt to write the queue is made. If
/// it fails, the data is sent later, after the system has given notification that
/// it is ready. This function returns WINSOCK_ERROR or WINSOCK_NOERROR.
//
int TDatagramSocket::Write(char* data, int& charsToWrite, TSocketAddress& outSocketAddress,
bool /*becomeOwnerOfData*/, bool /*copyData*/)
{
// Note that bBecomeOwnerOfdata and bCopyData are ignored if we are using DataQueues.
// Note that thus function may block here if blocking is enabled.
//
///PP need to enable flags use.
int charsSent = TWinSockDll::sendto(Handle, data, charsToWrite, 0,
&outSocketAddress, sizeof(sockaddr));
if (charsSent == SOCKET_ERROR) {
//It is entirely possible to get a blocking error here. For example, a call to
// send() could have been made right after the message was posted by the driver,
// but before this notification function got called.
//
charsToWrite = 0;
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK)
return WINSOCK_NOERROR;
return WINSOCK_ERROR;
}
charsToWrite = charsSent;
return WINSOCK_NOERROR;
}
//
/// Simply calls the other Write() function with the latest address. Arguments and
/// return values are the same.
//
int TDatagramSocket::Write(char* data, int& charsToWrite, bool becomeOwnerOfData, bool copyData)
{
return Write(data, charsToWrite, PeerSocketAddress, becomeOwnerOfData, copyData);
}
//
/// This function is called whenever the socket receives a read notification. This
/// means that data on the port is ready to be read. This function doesn't do much
/// with the error parameter. It simply doesn't do the read if there is an error
/// value. According to Winsock documentation, this error may be "any error in winsock.h".
//
int TDatagramSocket::DoReadNotification(const SOCKET& /*socket*/, int error)
{
if (error) {
LastError = error; // TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
// This function dan't do anything on its own without data queues. Without
// DataQueues, you can do two things: 1) subclass TDatagramSocket and write
// your own DoReadNotification() or 2) Have the socket redirect the FD_READ
// notification to the window of your choice with Socket::SetNotificationSet()
// and SetNotificationWindow().
//
return WINSOCK_NOERROR;
}
//
/// This function is called whenever the socket receives a write notification.
//
int TDatagramSocket::DoWriteNotification(const SOCKET& /*s*/, int error)
{
if (error) {
LastError = error; // TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
// This function dan't do anything on its own without data queues. Without
// DataQueues, you can do two things: 1) subclass TDatagramSocket and write
// your own DoWriteNotification() or 2) Have the socket redirect the FD_WRITE
// notification to the window of your choice with Socket::SetNotificationSet()
// and SetNotificationWindow().
//
return WINSOCK_NOERROR;
}
//----------------------------------------------------------------------------
//
/// This function calls TSocket constructor and initializes the state of the
/// connection to not connected.
//
TStreamSocket::TStreamSocket()
:
TSocket(),
ConnectStatus(NotConnected)
{
}
//
/// This function is an alias constructor.
//
TStreamSocket::TStreamSocket(SOCKET& src)
:
TSocket(src)
{
///!PP Important: Unless there is a way I haven't found yet, you can't tell
// what the ConnectStatus is, given just a SOCKET descriptor.
// Needs to be set somehow:
// nConnectStatus = ???;
}
//
// Constructor for a protocol defined by an int.
//
TStreamSocket::TStreamSocket(TSocketAddress& socketAddress, int addressFormat, int type, int protocol)
:
TSocket(socketAddress, addressFormat, type, protocol),
ConnectStatus(NotConnected)
{
}
//
/// This function copies the socket connection information.
//
TStreamSocket& TStreamSocket::operator =(TStreamSocket& src) // !CQ const?
{
TSocket::operator =(src);
return *this;
}
//
/// This reads from the already received and queued data. This data has already been
/// received from the socket driver.
//
int TStreamSocket::Read(char* data, int& charsToRead)
{
// Try to receive the characters.
//
int charsReceived = TWinSockDll::recv(Handle, data, charsToRead, 0); ///PP I Need to allow the use of flags here.
if (charsReceived == SOCKET_ERROR) {
// It is entirely possible to get a blocking error here. For example, a call to
// recv() could have been made right after the message was posted by the driver,
// but before this notification function got called.
//
charsToRead = 0; // This will be available for the caller to examine.
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK)
return WINSOCK_NOERROR;
// Some other error occurred.
//
return WINSOCK_ERROR;
}
charsToRead = charsReceived;
return WINSOCK_NOERROR;
}
//
/// This function writes the buffer into the stream.
//
int TStreamSocket::Write(char* data, int& charsToWrite, int flags,
bool /*becomeOwnerOfData*/, bool /*copyData*/)
{
int charsSent = TWinSockDll::send(Handle, data, charsToWrite, flags);
if (charsSent == SOCKET_ERROR) {
// It is entirely possible to get a blocking error here. For example, a call to
// send() could have been made right after the message was posted by the driver,
// but before this notification function got called.
//
charsToWrite = 0;
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK) {
return WINSOCK_NOERROR;
}
return WINSOCK_ERROR;
}
charsToWrite = charsSent;
return WINSOCK_NOERROR;
}
//
/// This function works just like the Read() function, but it works on the OOB
/// queue.
//
int TStreamSocket::ReadOOB(char* data, int& charsToRead)
{
// Try to receive the characters.
///PP I Need to allow the use of flags here.
//
int charsReceived = TWinSockDll::recv(Handle, data, charsToRead, MSG_OOB);
if (charsReceived == SOCKET_ERROR) {
// It is entirely possible to get a blocking error here. For example, a call to
// recv() could have been made right after the message was posted by the driver,
// but before this notification function got called.
//
charsToRead = 0;
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK)
return WINSOCK_NOERROR;
// Some other error occurred.
return WINSOCK_ERROR;
}
charsToRead = charsReceived;
return WINSOCK_NOERROR;
}
//
/// Works just like TStreamSocket::Write(), except it adds in the MSG_OOB into the
/// flags.
///
/// Perform write operation using out-of-band data (i.e. via a logically
/// independent tranmission channel between the connected sockets).
/// \note For 'non urgent data', you may used TStreamSocket::Write() instead.
//
int TStreamSocket::WriteOOB(char* data, int& charsToWrite, int flags,
bool /*becomeOwnerOfData*/, bool /*copyData*/)
{
flags |= MSG_OOB;
int charsSent = TWinSockDll::send(Handle, data, charsToWrite, flags);
if (charsSent == SOCKET_ERROR) {
// It is entirely possible to get a blocking error here. For example, a call to
// send() could have been made right after the message was posted by the driver,
// but before this notification function got called.
//
charsToWrite = 0;
LastError = TWinSockDll::WSAGetLastError();
if (LastError == WSAEWOULDBLOCK) {
return WINSOCK_NOERROR;
}
return WINSOCK_ERROR;
}
charsToWrite = charsSent;
return WINSOCK_NOERROR;
}
//
/// This function puts this socket into a passive "listening" mode.
//
int TStreamSocket::Listen(int maxQueuedConnections)
{
if (TWinSockDll::listen(Handle, maxQueuedConnections)) {
LastError = TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
ConnectStatus = Listening;
return WINSOCK_NOERROR;
}
//
/// This function sets myPeerSocketAddress to sAddressToConnectTo then calls
/// Connect(). (See Connect() for more details.)
//
int TStreamSocket::Connect(TSocketAddress& addressToConnectTo)
{
SetPeerSocketAddress(addressToConnectTo);
return Connect();
}
//
/// This function uses myPeerSocketAddress, which needs to be set before calling
/// this function. The connection attempt (and this function) should return right
/// away, without blocking. When actually connected, a notification comes from the
/// driver at the DoConnectNotification() function. Upon receiving that
/// notification, the nConnectStatus is set to nConnected. Technically, a datagram
/// socket can call connect; doing this sets the default address for future
/// send()/recv() calls that the datagram socket might use. It's in the
/// TStreamSocket class for simplicity, and because the TDatagramSocket class
/// already supports its own default address system.
//
int TStreamSocket::Connect()
{
if (TWinSockDll::connect(Handle, &PeerSocketAddress, sizeof(sockaddr))) {
LastError = TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
// The following code assumes that the socket is in non-blocking mode. If the socket was in
// blocking mode, then nConnectStatus would be nConnected. Thus, this code is slightly
// bugged and can be patched with a call to select() to determine whether the socket is
// really connected or not. Looked at another way, a blocking socket can be said to be
// connected if nConnectStatus is either nConnected or nConnectPending.
//
ConnectStatus = ConnectPending;
return WINSOCK_NOERROR;
}
//
/// This function will try to accept a connection with the first connecting peer
/// that is waiting in the queue. If successful, the TStreamSocket& socket will have
/// a valid and connected socket, a proper status of nConnected, and the correct
/// peer socket address. The caller usually calls this function in response to an
/// Accept notification. The caller merely needs to create a new TStreamSocket and
/// pass it to this function. The default constructor for TStreamSocket can be used,
/// because this function fixes up the missing parts.
//
int TStreamSocket::Accept(TStreamSocket& socket)
{
if (Accept(socket.Handle, socket.PeerSocketAddress) == WINSOCK_NOERROR) {
socket.ConnectStatus = Connected;
socket.SetSocketStyle(Family, Type, Protocol); //Copy our Family, etc. just to make sure.
return WINSOCK_NOERROR;
}
return WINSOCK_ERROR;
}
//
/// This function tries to accept a connection with the first connecting peer that
/// is waiting in the queue. If successful, the socket reference argument is set to
/// a new connected socket. The sAddress reference argument is set to the address of
/// the connecting peer. The caller of this function may immediately use the new
/// socket with data sends, etc.
///
/// Note that the caller may want to flag the socket as connected. If the socket
/// belongs to a StreamSocket, its nConnectionStatus can be set as nConnected. The
/// return value is either WINSOCK_ERROR or WINSOCK_NOERROR. If there is an error,
/// then nLastError will be set with the appropriate error.
///
/// Note that this call could be made when no pending socket connections are in the
/// queue. If this is the case, the call will block if the socket is marked as
/// blocking, and will return WINSOCK_ERROR with WSAEWOULDBLOCK if the socket is
/// marked as non-blocking. This function is usually called in response to an accept
/// notification. A socket is set up as a stream socket and listen() is called. When
/// a connection is ready, the driver notifies the listening socket with a
/// DoAcceptNotification() call. (See the DoAcceptNotification() call
/// documentation.) This Accept() function should be called as a result.
//
int TStreamSocket::Accept(SOCKET& socket, sockaddr& address)
{
int addressLength = sizeof(sockaddr);
socket = TWinSockDll::accept(Handle, &address, &addressLength);
if (socket == static_cast<SOCKET>(INVALID_SOCKET)) {
LastError = TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
return WINSOCK_NOERROR;
}
//
/// This function is called when the socket receives a read notification unless
/// there is an error. This means that data on the port is ready to be read.
/// This function doesn't do much with the nError parameter. It doesn't do the read
/// if there is an error value.
//
int TStreamSocket::DoReadNotification(const SOCKET& /*socket*/, int error)
{
if (error) {
LastError = error; // TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
return WINSOCK_NOERROR;
}
//
/// This function is called when the socket receives a write notification. This
/// means that data on the port is ready to be written.
//
int TStreamSocket::DoWriteNotification(const SOCKET& /*s*/, int error)
{
if (error) {
LastError = error; ///PP TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
// This function dan't do anything on its own without data queues. Without
// DataQueues, you can do two things: 1) subclass TStreamSocket and write
// your own DoWriteNotification() or 2) Have the socket redirect the FD_WRITE
// notification to the window of your choice with Socket::SetNotificationSet()
// and SetNotificationWindow().
//
return WINSOCK_NOERROR;
}
//
/// This notification appears when OOB data is ready to be received on the socket
/// port.
//
int TStreamSocket::DoOOBNotification(const SOCKET& /*s*/, int error)
{
if (error) {
LastError = error; // TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
// This function dan't do anything on its own without data queues. Without
// DataQueues, you can do two things: 1) subclass TStreamSocket and write
// your own DoOOBNotification() or 2) Have the socket redirect the FD_OOB
// notification to the window of your choice with Socket::SetNotificationSet()
// and SetNotificationWindow().
//
return WINSOCK_NOERROR;
}
//
/// This notification occurs when a client socket on the network is attempting to
/// connect to you. Code needs to be written to intercept this notification.
//
int TStreamSocket::DoAcceptNotification(const SOCKET& /*s*/, int /*error*/)
{
return 0; // We don't do anything. We let the pending acceptance sit there.
}
//
/// This means that the connection attempted with a server on the network has
/// completed. This function gets called sometime after this object makes a
/// connect() attempt. If the connect attempt was non-blocking, a notification is
/// posted. When this function gets called, the nConnectStatus should be
/// nConnecting.
//
int TStreamSocket::DoConnectNotification(const SOCKET& /*s*/, int error)
{
if (error) {
// There was an error, and we cannot connect; this may due to a number of reasons.
// We turn off our 'nConnectStatus' to 'nNotConnected'. This is becuase our attempt
// to connect failed.
//
ConnectStatus = NotConnected;
LastError = error; ///PP TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
ConnectStatus = Connected;
return WINSOCK_NOERROR;
}
//
/// This notification gets called when the socket has been closed. The socket is
/// marked as not connected, so the user can find out about it. It is important to
/// read any data that may be waiting in the queue before changing the status of the
/// connection and doing any notification.
//
int TStreamSocket::DoCloseNotification(const SOCKET& /*s*/, int error)
{
if (error) {
LastError = error; ///PP TWinSockDll::WSAGetLastError();
return WINSOCK_ERROR;
}
return WINSOCK_NOERROR;
}
} // OWL namespace
/* ========================================================================== */
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: ConnectStatus.
↑ V524 It is odd that the body of 'DoWriteNotification' function is fully equivalent to the body of 'DoReadNotification' function.