Cassova
Cassova

Reputation: 549

SocketAsyncEventArgs with binary/byte[] streams

I'm new to C# and sockets so I apologize if my questions are out of line. I started building a socket interface using the example in this link: https://code.msdn.microsoft.com/High-Performance-NET-69c2df2f

I want to be able to transfer binary files across the socket so I made an assumption (maybe the wrong one) that I should not use StringBuilder. I changed the OSUserToken from the original to use a MemoryStream and BinaryWriter (commenting out the original code).

Elsewhere in the code (from the link above), SocketAsyncEventArgs is intialized with SetBuffer(new Byte[_bufferSize], 0, _bufferSize);. I'm concerned this will not mesh well with my MemoryStream and BinaryWriter but it seems to work.

sealed class UserToken : IDisposable
{
    private Socket _ownerSocket;
    public Socket ownerSocket { get { return _ownerSocket; } }

    private MemoryStream _memoryStream;
    private BinaryWriter _binaryWriter;
    //private StringBuilder stringbuilder;

    private int totalByteCount;

    public String LastError;

    public UserToken(Socket readSocket, int bufferSize)
    {
        _ownerSocket = readSocket;
        _memoryStream = new MemoryStream();
        _binaryWriter = new BinaryWriter(_memoryStream);
        //stringbuilder = new StringBuilder(bufferSize);
    }

    // Do something with the received data, then reset the token for use by another connection.
    // This is called when all of the data have been received for a read socket.
    public void ProcessData(SocketAsyncEventArgs args)
    {
        String received = System.Text.Encoding.ASCII.GetString(_memoryStream.ToArray());
        //String received = stringbuilder.ToString();

        Debug.Write("Received: \"" + received + "\". The server has read " + received.Length + " bytes.");

        _memoryStream.SetLength(0);
        //stringbuilder.Length = 0;
        totalByteCount = 0;
    }

    public bool ReadSocketData(SocketAsyncEventArgs readSocket)
    {
        int byteCount = readSocket.BytesTransferred;

        /*
        if ((totalByteCount + byteCount) > stringbuilder.Capacity)
        {
            LastError = "Receive Buffer cannot hold the entire message for this connection.";
            return false;
        }
        else
        {
        */
            //stringbuilder.Append(Encoding.ASCII.GetString(readSocket.Buffer, readSocket.Offset, byteCount));
            _binaryWriter.Write(readSocket.Buffer,readSocket.Offset,byteCount);
            totalByteCount += byteCount;
            return true;
        /*}*/
    }

    public void Dispose()
    {
        _memoryStream.Dispose();
        _binaryWriter.Dispose();
        try
        {
            _ownerSocket.Shutdown(SocketShutdown.Both);
        }
        catch
        {
            //Nothing to do here, connection is closed already
        }
        finally
        {
            _ownerSocket.Close();
        }
    }
}

When I run this, it seems to work without an issue. Even if I set the protected const int DEFAULT_BUFFER_SIZE = 1 it will accept a stream of >1 bytes:

17:11:20:433 - Debug - Initializing the listener on port 5000...
17:11:20:439 - Debug - Starting the listener...
17:11:20:444 - Debug - Server started.
17:11:31:856 - Debug - Received: "listener". The server has read 8 bytes.
17:11:33:264 - Debug - Received: "l". The server has read 1 bytes.
17:11:33:268 - Debug - Received: "istener". The server has read 7 bytes.
17:11:36:744 - Debug - Received: "l". The server has read 1 bytes.
17:11:36:744 - Debug - Received: "i". The server has read 1 bytes.
17:11:36:746 - Debug - Received: "stener". The server has read 6 bytes.

My questions are these:

  1. Am I right that StringBuilder wouldn't work for binary files and I should use MemoryStream and BinaryWriter?
  2. Do I need to be concerned with a buffer overflow if elsewhere in the program, the SocketAsyncEventArgs is initialized with SetBuffer(new Byte[_bufferSize], 0, _bufferSize);?
  3. If I have to obey the buffer size limitation, do I need to put the same buffer restriction on my client sending data?

Upvotes: 0

Views: 436

Answers (1)

Cassova
Cassova

Reputation: 549

I found answers to my questions

  1. StringBuilder works fine. Just encode the strings in base64 before sending and decode after receiving. This should be done no matter if sending text or binary data. See the class I wrote below.
  2. Still don't know the answer to this question but seeing as StringBuilder & base64 works with binary, this question is no longer relevant.
  3. I think the answer to this question is yes. The client should have a max message length. I'm controlling based on the header portion of my socket where I define the length of the message. The header is fixed length and my max message length is 0xFFFFF.

Class for encoding/decoding base64:

public static class Base64
{
    public static string EncodeBase64(string text)
    {
        return System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text));
    }

    public static string EncodeBase64(byte[] array)
    {
        return System.Convert.ToBase64String(array);
    }

    public static string DecodeBase64ToString(string base64String)
    {
        return System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(base64String));
    }

    public static Byte[] DecodeBase64ToBinary(string base64String)
    {
        Byte[] bytes = System.Convert.FromBase64String(base64String);
        return bytes;
    }
}

Upvotes: 0

Related Questions