Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

482 lines
14 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
TranFile.c
Abstract:
The TransmitFile() routine sends a file over a connected socket
using asynchronous nonbufferred sends.
Author:
David Treadwell (davidtr) 29-July-1994
Revision History:
--*/
#include <windows.h>
#include <winsock.h>
#define MIN(a,b) ( (a) > (b) ? (b) : (a) )
//
// ASYNC_IO_LENGTH is the size of individual asynchronous I/O requests
// that we submit.
//
#define ASYNC_IO_LENGTH (32768)
//
// SYNC_IO_LENGTH is the size of individual synchronous I/O requests
// that we submit.
//
#define SYNC_IO_LENGTH (4096)
//
// PEND_COUNT is the maximum number of pending writes that we permit.
// This number should be at least 2 to ensure that there is always data
// available to be sent, but it should not be greater than
// MAXIMUM_WAIT_OBJECTS because we pass PEND_COUNT event objects to
// WaitForMultipleObjects().
//
#define PEND_COUNT (3)
//
// ASYNC_IO_THRESHOLD is the file size above which we'll use
// asynchronous I/O to send the file data. Below this size we use
// synchronous, bufferred I/O.
//
#define ASYNC_IO_THRESHOLD (10*1000)
//
// MULTI_CALL_THRESHOLD is the limit we'll use for sending the file
// data in a single I/O call. The single call is faster than multiple
// calls but at the expense of more physical resource utilization.
// Therefore, we don't use a single call unless the file is reasonably
// small.
//
#define MULTI_CALL_THRESHOLD (ASYNC_IO_LENGTH * PEND_COUNT)
BOOL
TransmitFile (
IN SOCKET SocketHandle,
IN LPTSTR FileName
)
/*++
Routine Description:
This routine transmits a file over a connected socket using
asynchronous nonbufferred I/O out of a memory-mapped file. This
technique minimizes the CPU cost of transmitting a file over a
socket by avoiding the buffer copy inherent in the bufferred I/O
which the synchronous send() routine imposes.
The calling thread is blocked until the remote end acknowledges
all of the transmitted file.
This routine's performance is not optimal in the case of making
multiple, repeated calls to it. It would be faster to have the
caller pass in the event handle array as a parameter to avoid
creatng the events in every call to this routine. Also, it would be
faster to have the caller set SO_SNDBUF to zero instead of doing it
here on every call.
For very small files this routine uses synchronous I/O because the
extra buffer copy is not important when the number of bytes is small
and the bufferring allows valuable overlap.
The memory-mapped file technique will not work for extremely large
files (size approaching 2GB) because they are too large to map into
memory--they consume all the availabl virtual address space
available to the process.
Arguments:
SocketHandle - a connected socket handle. This routine will change
the SO_SNDBUF setting of the socket to zero so that no bufferring
of sends occurs.
FileName - the name of the file to transmit over the socket. The
caller must have read access to the socket.
Return Value:
TRUE if the entire file is sent successfully. FALSE if an error
occurs, in which case the caller may obtain an error code with the
GetLastError() API.
--*/
{
HANDLE fileHandle;
BY_HANDLE_FILE_INFORMATION fileInformation;
HANDLE fileMapping;
PBYTE fileBuffer;
DWORD bytesSent;
DWORD fileSize;
INT i;
INT err;
BOOL ret;
INT bytesToSend;
DWORD bytesWritten;
DWORD bytesPending;
HANDLE events[PEND_COUNT];
OVERLAPPED overlapped[PEND_COUNT];
//
// Initialize locals so that we know how to clean up on exit.
//
fileHandle = NULL;
fileMapping = NULL;
fileBuffer = NULL;
for ( i = 0; i < PEND_COUNT; i++ ) {
events[i] = NULL;
}
//
// Do all work in a try-finally block so that cleanup happens
// correctly. This allows us to just return at any point when we're
// done and know that any opened objects will get cleaned up
// correctly.
//
try {
//
// Open the file that we're going to transmit.
//
fileHandle = CreateFile(
FileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( fileHandle == NULL ) {
return FALSE;
}
//
// Determine the size of the file.
//
if ( !GetFileInformationByHandle( fileHandle, &fileInformation ) ) {
return FALSE;
}
//
// Create a file mapping for the file and map it into the
// address space of this process. Using a memory-mapped file
// like this allows us to just send file data directly from the
// cache without worrying about additional I/O calls to read the
// file. The system automatically reads the file for us when it
// needs to do so.
//
fileMapping =
CreateFileMapping( fileHandle, NULL, PAGE_READONLY, 0, 0, NULL );
if ( fileMapping == NULL ) {
return FALSE;
}
fileBuffer = MapViewOfFile( fileMapping, FILE_MAP_READ, 0, 0, 0 );
if ( fileBuffer == NULL ) {
return FALSE;
}
//
// If the file is small, we'll just use normal, synchronous I/O
// to transmit the file. For small files, the overhead
// associated with asynchronous I/O outweighs the advantages.
//
// Note that we ignore the nFileSizeHigh field because files
// larger than 2GB will not successfully be mapped into the
// virtual address space of the process, so the above calls
// would have already failed.
//
fileSize = fileInformation.nFileSizeLow;
if ( fileSize < ASYNC_IO_THRESHOLD ) {
//
// Set the send buffer on the socket to be equal to the I/O
// size that we'll be using. This ensures that the system
// will efficiently allocate buffers for the send as well as
// block this thread minimally.
//
i = SYNC_IO_LENGTH;
err = setsockopt( SocketHandle, SOL_SOCKET, SO_SNDBUF,
(char *)&i, sizeof(i) );
if ( err < 0 ) {
return FALSE;
}
for ( bytesSent = 0; bytesSent < fileSize; bytesSent += i ) {
bytesToSend = MIN( SYNC_IO_LENGTH, fileSize - bytesSent );
i = send( SocketHandle, fileBuffer + bytesSent, bytesToSend, 0 );
if ( i < 0 ) {
return FALSE;
}
}
//
// The entire file was transmitted successfully.
//
return TRUE;
}
//
// We're going to use asynchronous I/O to transmit the file.
// Set the send buffer size on the socket to zero. This
// prevents send bufferring from occurring. Normally
// applications want send bufferring, but because we're going to
// be very careful about keeping plenty of data around for
// TCP/IP to send and we want maximum performance, avoiding the
// extra buffer copy is beneficial.
//
i = 0;
err = setsockopt( SocketHandle, SOL_SOCKET, SO_SNDBUF,
(char *)&i, sizeof(i) );
if ( err < 0 ) {
return FALSE;
}
//
// If the file isn't too big, send it all with a single
// WriteFile() call.
//
if ( fileSize < MULTI_CALL_THRESHOLD ) {
//
// Initialize the overlapped structure that we'll use for
// the I/O. Rather than waiting on an event, we'll just use
// the GetOverlappedResult() API to learn of I/) completion.
//
overlapped[0].Internal = 0;
overlapped[0].InternalHigh = 0;
overlapped[0].Offset = 0;
overlapped[0].OffsetHigh = 0;
overlapped[0].hEvent = NULL;
//
// Send the entire file in one big chunk. The system will
// send it directly out of the file cache onto the wire.
//
ret = WriteFile(
(HANDLE)SocketHandle,
fileBuffer,
fileSize,
&bytesWritten,
&overlapped[0]
);
if ( !ret ) {
//
// If the write failed with any error except the one
// which indicates that the I/O was successfully pended,
// fail.
//
if ( GetLastError( ) != ERROR_IO_PENDING ) {
return FALSE;
}
//
// Wait for the pended write operation and determine
// whether it was successful.
//
ret = GetOverlappedResult(
(HANDLE)SocketHandle,
&overlapped[0],
&bytesWritten,
TRUE
);
if ( !ret ) {
return FALSE;
}
}
//
// The entire file was transmitted successfully.
//
return TRUE;
}
//
// This is a big file that we're going to use multiple
// asynchronous I/O calls to transmit. Open the events that
// we'll use to wait for I/O to complete and initialize the
// overlapped structures that we'll use.
//
for ( i = 0; i < PEND_COUNT; i++ ) {
events[i] = CreateEvent( NULL, FALSE, TRUE, NULL );
if ( events[i] == NULL ) {
return FALSE;
}
overlapped[i].Internal = 0;
overlapped[i].InternalHigh = 0;
overlapped[i].Offset = 0;
overlapped[i].OffsetHigh = 0;
overlapped[i].hEvent = events[i];
}
//
// Loop through the file data writing it to the socket
// asynchronously. Whenever a write completes, pend a new write
// request so that we always have several writes outstanding and
// TCP/IP always has data available to send.
//
bytesSent = 0;
bytesPending = 0;
while ( TRUE ) {
//
// Wait for one of the I/O events to become set. This signals
// us that we can send some data on the socket.
//
i = WaitForMultipleObjects(
PEND_COUNT,
events,
FALSE,
INFINITE
) - WAIT_OBJECT_0;
//
// Determine if the pended write operation was successful.
//
ret = GetOverlappedResult(
(HANDLE)SocketHandle,
&overlapped[i],
&bytesWritten,
FALSE
);
if ( !ret ) {
return FALSE;
}
//
// Update the count of bytes that we've sent from the file
// thus far. Note that if this is the first pass through
// the loop, GetOverlappedResult() will return 0 bytes
// written because no actual WriteFile() calls have been
// made yet, and we initialized the overlapped structures
// above.
//
bytesSent += bytesWritten;
bytesPending -= bytesWritten;
//
// Check whether we have completed sending the file.
//
if ( bytesSent == fileSize ) {
return TRUE;
}
//
// Check whether we're nearly done sending the file and have
// pended writes for all the file data. If this is the case,
// just reset the event which got hit and wait for the
// remainder of the pended sends to complete.
//
if ( bytesSent + bytesPending == fileSize ) {
ret = ResetEvent( events[i] );
if ( !ret ) {
return FALSE;
}
continue;
}
//
// Issue a write from the file buffer to the socket.
// Typically, this write will return ERROR_IO_PENDING which
// means that the write is in progress and has not completed
// yet. The event will be set by the system when the write
// completes.
//
bytesToSend =
MIN( ASYNC_IO_LENGTH, fileSize - (bytesSent + bytesPending) );
ret = WriteFile(
(HANDLE)SocketHandle,
fileBuffer + bytesSent + bytesPending,
bytesToSend,
&bytesWritten,
&overlapped[i]
);
if ( !ret && GetLastError( ) != ERROR_IO_PENDING ) {
return FALSE;
}
bytesPending += bytesToSend;
}
} finally {
//
// Close open handles, etc.
//
if ( fileMapping != NULL ) {
UnmapViewOfFile( fileBuffer );
}
if ( fileMapping != NULL ) {
CloseHandle( fileMapping );
}
if ( fileHandle != NULL ) {
CloseHandle( fileHandle );
}
for ( i = 0; i < PEND_COUNT; i++ ) {
if ( events[i] != NULL ) {
CloseHandle( events[i] );
}
}
}
} // TransmitFile