-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Remove Socket.SendFile 2GB limitation on Windows by partitioning large files #120634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
// Send postBuffer if present, or apply disconnect/reuse flags to last operation | ||
if (postBuffer.Length > 0) | ||
{ | ||
bool success = TransmitFileHelper(handle, null, null, IntPtr.Zero, 0, (IntPtr)postPinnedBuffer, postBuffer.Length, flags); | ||
if (!success) | ||
{ | ||
return GetLastSocketError(); | ||
} | ||
} | ||
else if ((flags & (TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket)) != 0) | ||
{ | ||
// If no postBuffer but disconnect/reuse flags are set, send an empty buffer with the flags | ||
bool success = TransmitFileHelper(handle, null, null, IntPtr.Zero, 0, IntPtr.Zero, 0, flags); | ||
if (!success) | ||
{ | ||
return GetLastSocketError(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot, what about the other TransmitFileFlags? If any are specified, might they need to be passed to the other TransmitFileHelper calls, not just the last one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - I've separated the flags into behavior flags (WriteBehind, UseSystemThread, UseKernelApc) and terminal flags (Disconnect, ReuseSocket). Behavior flags now apply to all TransmitFileHelper calls for consistent performance characteristics, while terminal flags only apply to the final operation. Fixed in commit e5b8a72.
return GetLastSocketError(); | ||
} | ||
} | ||
else if ((flags & (TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket)) != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot, is splitting this if/else necessary? What would happen if you just always did the if block, regardless of whether postBuffer.Length > 0? Wouldn't that be the same thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch - the if/else split was unnecessary. Now the postBuffer send always happens with the full flags, regardless of whether postBuffer is empty or not. This simplifies the logic. Fixed in commit e5b8a72.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR removes the 2GB file size limitation for Socket.SendFile on Windows by implementing automatic file partitioning. The Windows TransmitFile API is limited to int.MaxValue bytes, but the solution partitions large files into multiple chunks while maintaining proper handling of pre/post buffers and flags.
- Implemented partitioning logic to handle files larger than 2GB by splitting them into manageable chunks
- Enhanced the TransmitFileHelper method to accept explicit byte counts for precise chunk transmission
- Enabled the previously skipped test case for files greater than 2GB on Windows
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
File | Description |
---|---|
src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs | Removed ActiveIssue attribute to enable >2GB file test on Windows |
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs | Added file partitioning logic and updated TransmitFileHelper signature |
src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj | Added reference to SetFilePointerEx interop for file positioning |
// Set the file pointer to the current offset | ||
if (!Interop.Kernel32.SetFilePointerEx(fileHandle!, offset, out _, 0 /* FILE_BEGIN */)) | ||
{ | ||
return GetLastSocketError(); |
Copilot
AI
Oct 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When SetFilePointerEx fails, GetLastSocketError() returns socket-specific errors but SetFilePointerEx is a file operation. Consider using Marshal.GetLastWin32Error() or GetLastPInvokeError() to get the actual file operation error code.
return GetLastSocketError(); | |
// Use Marshal.GetLastWin32Error() for file operation errors | |
return (SocketError)Marshal.GetLastWin32Error(); |
Copilot uses AI. Check for mistakes.
// Send preBuffer if present | ||
if (preBuffer.Length > 0) | ||
{ | ||
bool success = TransmitFileHelper(handle, null, null, (IntPtr)prePinnedBuffer, preBuffer.Length, IntPtr.Zero, 0, TransmitFileOptions.UseDefaultWorkerThread); |
Copilot
AI
Oct 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'success' is reused in multiple scopes within the same method. Consider using more descriptive names like 'preBufferSent', 'postBufferSent', and 'flagsApplied' to improve code clarity.
Copilot uses AI. Check for mistakes.
// Send postBuffer if present, or apply disconnect/reuse flags to last operation | ||
if (postBuffer.Length > 0) | ||
{ | ||
bool success = TransmitFileHelper(handle, null, null, IntPtr.Zero, 0, (IntPtr)postPinnedBuffer, postBuffer.Length, flags); |
Copilot
AI
Oct 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'success' is reused in multiple scopes within the same method. Consider using more descriptive names like 'preBufferSent', 'postBufferSent', and 'flagsApplied' to improve code clarity.
Copilot uses AI. Check for mistakes.
else if ((flags & (TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket)) != 0) | ||
{ | ||
// If no postBuffer but disconnect/reuse flags are set, send an empty buffer with the flags | ||
bool success = TransmitFileHelper(handle, null, null, IntPtr.Zero, 0, IntPtr.Zero, 0, flags); |
Copilot
AI
Oct 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'success' is reused in multiple scopes within the same method. Consider using more descriptive names like 'preBufferSent', 'postBufferSent', and 'flagsApplied' to improve code clarity.
Copilot uses AI. Check for mistakes.
…operation Co-authored-by: stephentoub <[email protected]>
Summary: Remove Socket.SendFile 2GB limitation on Windows
Problem
On Windows, the
Socket.SendFile
API was limited to sending files up toint.MaxValue
(2GB) due to the Win32TransmitFile
function'sint numberOfBytesToWrite
parameter. This limitation did not exist on Linux/macOS. The issue has been tracked as #42534.Solution
Implemented automatic partitioning of large file sends into multiple
TransmitFile
calls when the file exceeds 2GB, as suggested by the TransmitFile Win32 API documentation.Implementation Details
Modified:
SocketPal.Windows.cs::SendFile
int.MaxValue
usingRandomAccess.GetLength
int.MaxValue
-sized chunks (with behavior flags):SetFilePointerEx
TransmitFile
with exact chunk sizeTransmitFile
callModified:
TransmitFileHelper
numberOfBytesToWrite
parameter (defaults to 0 for "send entire file")Modified:
System.Net.Sockets.csproj
Interop.SetFilePointerEx.cs
to enable file positioningModified:
SendFile.cs
tests[ActiveIssue]
attribute fromGreaterThan2GBFile_SendsAllBytes
testTesting
Changes
SocketPal.Windows.cs
to partition large file sendsTransmitFileHelper
to accept numberOfBytesToWrite parameterOriginal prompt
Fixes #42534
💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.