Ejemplo n.º 1
0
OverlappedTransferBuffer *Socket::BeginSend(int maxBytesToSend)
{
	if (!writeOpen)
		return 0;

	// See if the oldest one of the previously submitted transfers has now finished,
	// and reuse that buffer without allocating a new one, if so.
#ifdef WIN32
	if (queuedSendBuffers.Size() > 0)
	{
		OverlappedTransferBuffer *sentData = *queuedSendBuffers.Front();
		DWORD flags = 0;
		BOOL ret = WSAGetOverlappedResult(connectSocket, &sentData->overlapped, (LPDWORD)&sentData->bytesContains, 
			(queuedSendBuffers.CapacityLeft() == 0) ? TRUE : FALSE, &flags);
		int error = (ret == TRUE) ? 0 : Network::GetLastError();
		if (ret == TRUE)
		{
			queuedSendBuffers.PopFront();

            // If the buffer we pulled off was too small, free it and allocate a new one which is of the desired size.
            if (sentData->bytesAllocated < maxBytesToSend)
            {
                DeleteOverlappedTransferBuffer(sentData);
	            return AllocateOverlappedTransferBuffer(maxBytesToSend); ///\todo In debug mode - track this pointer.
            }
            else
            {
                // The existing transfer buffer is large enough. Prepare it for reuse and pass back to caller.
			    sentData->buffer.len = sentData->bytesAllocated; // This is the number of bytes that the client is allowed to fill.
			    sentData->bytesContains = 0; // No bytes currently in use.

			    return sentData;
            }
		}
		if (ret == FALSE && error != WSA_IO_INCOMPLETE)
		{
			LOG(LogError, "Socket::BeginSend: WSAGetOverlappedResult failed with an error %s, code %d != WSA_IO_INCOMPLETE!", 
				Network::GetErrorString(error).c_str(), error);
			writeOpen = false;
			return 0;
		}
	}

	if (queuedSendBuffers.CapacityLeft() == 0)
		return 0;
#endif

	// No previous send buffer has finished from use (or not using overlapped transfers) - allocate a new buffer.
	return AllocateOverlappedTransferBuffer(maxBytesToSend);
}
Ejemplo n.º 2
0
OverlappedTransferBuffer *Socket::BeginSend()
{
	if (!writeOpen)
		return 0;

	// See if the oldest one of the previously submitted transfers has now finished,
	// and reuse that buffer without allocating a new one, if so.
#ifdef WIN32
	if (queuedSendBuffers.Size() > 0)
	{
		OverlappedTransferBuffer *sentData = *queuedSendBuffers.Front();
		DWORD flags = 0;
		BOOL ret = WSAGetOverlappedResult(connectSocket, &sentData->overlapped, (LPDWORD)&sentData->bytesContains, 
			(queuedSendBuffers.CapacityLeft() == 0) ? TRUE : FALSE, &flags);
		int error = (ret == TRUE) ? 0 : Network::GetLastError();
		if (ret == TRUE)
		{
			queuedSendBuffers.PopFront();
			sentData->buffer.len = maxSendSize; // This is the number of bytes that the client is allowed to fill.
			sentData->bytesContains = 0; // No bytes currently in use.
			return sentData;
		}
		if (ret == FALSE && error != WSA_IO_INCOMPLETE)
		{
			LOG(LogError, "Socket::BeginSend: WSAGetOverlappedResult failed with an error %s, code %d != WSA_IO_INCOMPLETE!", 
				Network::GetErrorString(error).c_str(), error);
			writeOpen = false;
			return 0;
		}
	}

	if (queuedSendBuffers.CapacityLeft() == 0)
		return 0;
#endif

	// No previous send buffer has finished from use (or not using overlapped transfers) - allocate a new buffer.
	OverlappedTransferBuffer *transfer = AllocateOverlappedTransferBuffer(maxSendSize);
	return transfer; ///\todo In debug mode - track this pointer.
}
Ejemplo n.º 3
0
OverlappedTransferBuffer *Socket::BeginReceive()
{
	// UDP 'slave socket' is a socket descriptor on the server side that is a copy of the single UDP server listen socket.
	// The slave sockets don't receive data directly, but the server socket is used instead to receive data for them.
	if (IsUDPSlaveSocket())
	{
#ifdef WIN32
		assert(queuedReceiveBuffers.Size() == 0); // We shouldn't ever have queued a single receive buffer for this Socket.
#endif
		return 0; // If we happen to come here, act as if the socket never received any data.
	}

#ifdef WIN32
	if (readOpen)
	{
		// Insert new empty receive buffers to the Overlapped Transfer receive queue until we have a full capacity queue primed.
		const int capacityLeft = queuedReceiveBuffers.CapacityLeft();
		for(int i = 0; i < capacityLeft; ++i)
			EnqueueNewReceiveBuffer();
	}

	if (queuedReceiveBuffers.Size() == 0)
		return 0;

	OverlappedTransferBuffer *receivedData = *queuedReceiveBuffers.Front();
	DWORD flags = 0;
	BOOL ret = WSAGetOverlappedResult(connectSocket, &receivedData->overlapped, (LPDWORD)&receivedData->bytesContains, FALSE, &flags);
	int error = (ret == TRUE) ? 0 : Network::GetLastError();
	if (ret == TRUE)
	{
		queuedReceiveBuffers.PopFront();

		// Successfully receiving zero bytes with overlapped sockets means the same as recv() of 0 bytes,
		// peer has closed the write connection (but can still recv() until we also close the write connection).
		if (receivedData->bytesContains == 0)
		{
			DeleteOverlappedTransferBuffer(receivedData);
            // Urho3D: only close TCP sockets upon receiving 0 bytes
            if (transport == SocketOverTCP && readOpen)
            {
                LOG(LogInfo, "Socket::BeginReceive: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().c_str());
                readOpen = false;
            }
			return 0;
		}

		return receivedData;
	}
	else if (error == WSAEDISCON)
	{
		queuedReceiveBuffers.PopFront();
		DeleteOverlappedTransferBuffer(receivedData);
		if (readOpen || writeOpen)
			LOG(LogError, "Socket::BeginReceive: WSAEDISCON. Bidirectionally closing connection in socket %s.", ToString().c_str());
		if (IsUDPServerSocket())
			LOG(LogError, "Socket::BeginReceive: Unexpected: Received WSAEDISCON on UDP server socket!");
		Close();
		return 0;
	}
	else if (error != WSA_IO_INCOMPLETE)
	{
		queuedReceiveBuffers.PopFront();
		if (readOpen || writeOpen)
			if (!(IsUDPServerSocket() && error == 10054)) // If we are running both UDP server and client on localhost, we can receive 10054 (Peer closed connection) on the server side, in which case, we ignore this error print.
				LOG(LogError, "Socket::BeginReceive: WSAGetOverlappedResult failed with code %d when reading from an overlapped socket! Reason: %s.", error, Network::GetErrorString(error).c_str());
		DeleteOverlappedTransferBuffer(receivedData);
		// Mark this socket closed, unless the read error was on a UDP server socket, in which case we must ignore
		// the read error on this buffer (an error on a single client connection cannot shut down the whole server!)
		if (!IsUDPServerSocket() && (readOpen || writeOpen))
		{
			LOG(LogError, "Socket::BeginReceive: Closing socket due to read error!");
			Close();
		}
	}
	return 0;
#else
	if (!readOpen)
		return 0;

	const int receiveBufferSize = 4096;
	OverlappedTransferBuffer *buffer = AllocateOverlappedTransferBuffer(receiveBufferSize);
	EndPoint source;
	buffer->bytesContains = Receive(buffer->buffer.buf, buffer->buffer.len, &source);
	if (buffer->bytesContains > 0)
	{
		buffer->fromLen = sizeof(buffer->from);
		buffer->from = source.ToSockAddrIn();
		return buffer;
	}
	else
	{
		// Did not get any data. Delete the buffer immediately.
		DeleteOverlappedTransferBuffer(buffer);
		return 0;
	}
#endif
}
Ejemplo n.º 4
0
void Socket::EnqueueNewReceiveBuffer(OverlappedTransferBuffer *buffer)
{
	if (!readOpen || queuedReceiveBuffers.CapacityLeft() == 0 || IsUDPSlaveSocket())
	{
		DeleteOverlappedTransferBuffer(buffer); // buffer may be a zero pointer, but that is alright.
		return;
	}

	if (!buffer)
	{
		const int receiveBufferSize = 16384; // This is best to be at least 9K (the largest size of jumbo datagrams commonly supported)
		buffer = AllocateOverlappedTransferBuffer(receiveBufferSize);
		if (!buffer)
		{
			LOG(LogError, "Socket::EnqueueNewReceiveBuffer: Call to AllocateOverlappedTransferBuffer failed!");
			return;
		}
	}

	if (WSAResetEvent(buffer->overlapped.hEvent) != TRUE)
		LOG(LogError, "Socket::EnqueueNewReceiveBuffer: WSAResetEvent failed!");

	unsigned long flags = 0;
	int ret;

	if (IsUDPServerSocket())
	{
		buffer->fromLen = sizeof(buffer->from);
		ret = WSARecvFrom(connectSocket, &buffer->buffer, 1, (LPDWORD)&buffer->bytesContains, &flags, (sockaddr*)&buffer->from, &buffer->fromLen, &buffer->overlapped, 0);
	}
	else
	{
		ret = WSARecv(connectSocket, &buffer->buffer, 1, (LPDWORD)&buffer->bytesContains, &flags, &buffer->overlapped, 0);
	}

	int error = (ret == 0) ? 0 : Network::GetLastError();
	if (ret == 0 || (ret == SOCKET_ERROR && error == WSA_IO_PENDING)) // If ret is not 0, ret == SOCKET_ERROR according to MSDN. http://msdn.microsoft.com/en-us/library/ms741688(VS.85).aspx
	{
		if (ret == 0 && buffer->bytesContains == 0)
		{
            // Urho3D: only close TCP sockets upon receiving 0 bytes
            if (transport == SocketOverTCP && readOpen)
            {
                LOG(LogInfo, "Socket::EnqueueNewReceiveBuffer: Received 0 bytes from the network. Read connection closed in socket %s.", ToString().c_str());
                readOpen = false;
            }
			DeleteOverlappedTransferBuffer(buffer);
			return;
		}
		// Return value 0: The operation completed and we have received new data. Push it to a queue for the user to receive.
		// WSA_IO_PENDING: The operation was successfully enqueued.
		bool success = queuedReceiveBuffers.Insert(buffer);
		if (!success)
		{
			LOG(LogError, "Socket::EnqueueNewReceiveBuffer: queuedReceiveBuffers.Insert(buffer); failed!");
			DeleteOverlappedTransferBuffer(buffer);
		}
	}
	else if (error == WSAEDISCON)
	{
		if (IsUDPServerSocket())
			LOG(LogError, "Unexpected: Received WSAEDISCON on a UDP server socket!");

		LOG(LogError, "Socket::EnqueueNewReceivebuffer: WSAEDISCON. Connection closed in socket %s.", ToString().c_str());
		readOpen = false;
		///\todo Should do writeOpen = false; here as well?
		DeleteOverlappedTransferBuffer(buffer);
		return;
	}
	else
	{
		if (error != WSAEWOULDBLOCK && error != 0)
		{
			LOG(LogError, "Socket::EnqueueNewReceiveBuffer: %s for overlapped socket %s failed! Error: %s.", IsUDPServerSocket() ? "WSARecvFrom" : "WSARecv", ToString().c_str(), Network::GetErrorString(error).c_str());

			// We never close the server socket as a reaction on any error, since an error on one client could shut down
			// the whole server for all clients. This check is mainly here to ignore the 10054 error (WSAECONNRESET) which
			// is returned when UDP clients give back a ICMP Host Unreachable message, but since there may be other
			// client-specific errors, we don't explicitly check for the 10054 case only.
			if (!IsUDPServerSocket())
			{
				LOG(LogError, "Socket::EnqueueNewReceiveBuffer: Closing down socket.",  Network::GetErrorString(error).c_str());
				readOpen = false;
				writeOpen = false;
				Close();
			}
		}

		DeleteOverlappedTransferBuffer(buffer); // We failed to queue the buffer, free it up immediately to avoid leaking memory.
		return;
	}
}