They are re-usable, but only after the send request that used the arguments has completed (or returned an error!)
These args are supposed to be reusable
If you re-use a SocketAsyncEventArgs before the first operation that it's used on completes, all kinds of bad behavior may occur, including you not sending the data you think you're sending.
If you keep one buffer per SocketAsyncEventArgs and keep track of completed send args in a queue where you can re-use them when next sending (and create a new buffer/args pair if the queue is empty) then your system will work correctly.
Right, I was implementing it wrong for sending but correctly for receiving. I have a class called SocketState that contains the socket and other important info pertaining to the connection. At the time the SocketState is created, args for both sending and receiving are retrieved from a Stack and assigned to that SocketState for the duration of its existence. They're reset and returned to the Stack on disposal. I used a dedicated buffer for receiving but for sending I was calling SetBuffer and changed the underlying buffer on each send call by setting the buffer to the byte array of data to be sent. It was done in a thread safe context using locking and a queuing mechanism so the next send would NOT execute until after the previous send's callback had completed. Surprisingly enough, this flawed implementation was NOT an issue on .NET 3.5 and was NOT an issue for TCP on .NET 4.0/4.5. Therefore, this is how it managed to elude me for a couple days...
Once I assigned a dedicated buffer for sending, and used Buffer.BlockCopy to copy the byte array to be sent to the buffer, it worked flawlessly.