none
How make a Async Socket Server compatible to receive binary data and string? RRS feed

  • Question

  • I have a client android (Java) that firstly sends to server a string with a delimited prefix "|CONNECTION|" with device information, the server process fine and show the result correctly.

    Now i want know how make this same logic with a binary data (a bitmap in my case), i tried make this, but when android sends

    dos.writeInt(array.length);

    or

    dos.write(array, 0, array.length);

    i have a error in my switch...case

    void server_Received(Listener l, Info i, string received)
    {
    
    string[] cmd = received.Split('|');
    
    switch (cmd[1]) {
    
    }
    
    }


    that says something like: 

    index out of range

    already when android sends:

    dos.writeBytes("|PRINT|");

    works fine, similar to when "|CONNECTION|" is received.

    Then i want know what the solution to make works this switch...case of server_Received(); routine?

    I have saw this question (about android code) and tested, but the result is the same of my following code.

    The other possible solution can be create a other Async Socket Server and other Socket on android, only to send/receive this bitmap, but belive that still possible receive in same socket (string and binary data).

    Also already was suggested use a ObjectOutputStream (android), on attempt of send all only one time, and not separated (like already was made), so cmd[1] on server probably could be |PRINT|. But i still not tested this approach.

    Follow is all relevant code (C# Server and Client Java android):

    Form:

    namespace MyServer
    {
        public partial class frmMain : Form
        {
            Listener server;
            Thread startListen;
    
            Bitmap _buffer;
    		
    		public frmMain()
            {
                InitializeComponent();
                server = new Listener();
            }
    		
    		private void startToolStripMenuItem_Click(object sender, EventArgs e)
            {
                startListen = new Thread(listen);
                startListen.Start();
            }
    
            void listen()
            {
                server.BeginListen(101); // Listen on port 101
                server.Received += new Listener.ReceivedEventHandler(server_Received);
                server.Disconnected += new Listener.DisconnectedEventHandler(server_Disconnected);
            }
    
            void server_Disconnected(Listener l, Info i)
            {
                Invoke(new _Remove(Remove), i);
            }
    
            void server_Received(Listener l, Info i, string received)
            {
    			
    			string[] cmd = received.Split('|');
    
                switch (cmd[1])
                {
    				
    				case "CONNECTION":
                        
    					Invoke(new _Add(Add), i, cmd[2] + " - " + cmd[3] + " - " + cmd[4]);
    					
    					break;
    					
    				case "PRINT":
    
                        RecvScreenshot(i);
    
                        break;
                }
            }
    		
    		delegate void _Add(Info i, string device);
            void Add(Info i, string device)
            {
                string[] splitIP = i.RemoteAddress.Split(':');
                ListViewItem item = new ListViewItem();
                item.Text = i.ID.ToString();
                item.SubItems.Add(splitIP[0]);
                item.SubItems.Add(device);
    
                item.Tag = i;
    
                lvConnections.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
                .SetValue(lvConnections, true, null);
    
                lvConnections.Items.Add(item);
            }
    
            delegate void _Remove(Info i);
            void Remove(Info i)
            {
                foreach (ListViewItem item in lvConnections.Items)
                {
                    if ((Info)item.Tag == i)
                    {
                        item.Remove();
                        break;
                    }
                }
            }
    		
    		private void RecvScreenshot(Info i)
            {
    
                var lengthData = new byte[4];
                var lengthBytesRead = 0;
    
                while (lengthBytesRead < lengthData.Length)
                {
                    var read = i.sock.Receive(lengthData, lengthBytesRead, lengthData.Length - lengthBytesRead, SocketFlags.None);
                    if (read == 0) return;
                    lengthBytesRead += read;
                }
    
                Array.Reverse(lengthData);
                var length = BitConverter.ToInt32(lengthData, 0);
    
                if (length > 0)
                {
                    var imageData = new byte[length];
                    var imageBytesRead = 0;
    
                    while (imageBytesRead < imageData.Length)
                    {
                        var read = i.sock.Receive(imageData, imageBytesRead, imageData.Length - imageBytesRead, SocketFlags.None);
                        if (read == 0) return;
                        imageBytesRead += read;
                    }
    
                    using (var stream = new MemoryStream(imageData))
                    {
                        var bitmap = new Bitmap(stream);
                        Invoke(new ImageCompleteDelegate(ImageComplete), new object[] { bitmap });
                    }
                }
    
            }
    
            private delegate void ImageCompleteDelegate(Bitmap bitmap);
            private void ImageComplete(Bitmap bitmap)
            {
                if (_buffer != null)
                {
                    _buffer.Dispose();
                }
                _buffer = new Bitmap(bitmap);
                pictureBox1.Size = _buffer.Size;
                pictureBox1.Invalidate();
            }
    
            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                if (_buffer == null) return;
                e.Graphics.DrawImage(_buffer, 0, 0);
            }
    		
    	}
    }
    		

    Listener:

    class Listener
    {
        Socket s;
        public List<Info> clients;
    
        public delegate void ReceivedEventHandler(Listener l, Info i, string received);
        public event ReceivedEventHandler Received;
        public delegate void DisconnectedEventHandler(Listener l, Info i);
        public event DisconnectedEventHandler Disconnected;
    
        bool listening = false;
    
        public Listener()
        {
            clients = new List<Info>();
            s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
    
        public bool Running
        {
            get { return listening; }
        }
    
        public void BeginListen(int port)
        {
            s.Bind(new IPEndPoint(IPAddress.Any, port));
            s.Listen(100);
            s.BeginAccept(new AsyncCallback(AcceptCallback), s);
            listening = true;
        }
    
        public void StopListen()
        {
            if (listening == true)
            {
                s.Close();
                listening = false;
            }
        }
    
        void AcceptCallback(IAsyncResult ar)
        {
            Socket handler = (Socket)ar.AsyncState;
            Socket sock = handler.EndAccept(ar);
            Info i = new Info(sock);
            clients.Add(i);
    
            Console.WriteLine("New Connection: " + i.ID.ToString());
            i.Send("INFO" + Environment.NewLine);
    
            sock.BeginReceive(i.buffer, 0, i.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), i);
            handler.BeginAccept(new AsyncCallback(AcceptCallback), handler);
        }
    
        void ReadCallback(IAsyncResult ar)
        {
            Info i = (Info)ar.AsyncState;
            try
            {
                int rec = i.sock.EndReceive(ar);
                if (rec != 0)
                {
                    string data = Encoding.ASCII.GetString(i.buffer, 0, rec);
                    Received(this, i, data);
                }
                else
                {
                    Disconnected(this, i);
                    return;
                }
    
                i.sock.BeginReceive(i.buffer, 0, i.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), i);
            }
            catch (Exception ex)
            {
                Disconnected(this, i);
                i.sock.Close();
                clients.Remove(i);
            }
        }
    }

    Info:

    public class Info
    {
        public Socket sock;
        public Guid ID;
        public string RemoteAddress;
        public byte[] buffer = new byte[8192];
        
        public Info(Socket sock)
        {
            this.sock = sock; 
            ID = Guid.NewGuid(); 
            RemoteAddress = sock.RemoteEndPoint.ToString(); 
        }
    
        public void Send(string data)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(data);
            sock.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback((ar) =>
            {
                sock.EndSend(ar);
            }), buffer);
        }
    }

    Android (Java):

    while (clientSocket.isConnected()) {
    
    // Sending device information
    
    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(clientSocket.getOutputStream()));
    
    if (line.equalsIgnoreCase("INFO")) {
    
    	byte[] data = ("|CONNECTION|" + Build.MANUFACTURER + "|" + Build.MODEL + "|" + Build.VERSION.RELEASE).getBytes(StandardCharsets.UTF_8);
    	out.writeInt(data.length);
    	out.write(data, 0, data.length);
    	out.flush();
    
     }
    
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // Sending a bitmap in array
    
    DataOutputStream dos;
    
    try {
    	dos = new DataOutputStream(clientSocket.getOutputStream());
    	dos.writeBytes("|PRINT|");
    	dos.writeInt(array.length);
    	dos.write(array, 0, array.length);
    	dos.flush();
    } catch (IOException e) {
    	e.printStackTrace();
    }




    • Edited by FLASHCODER Tuesday, October 30, 2018 2:36 PM
    Tuesday, October 30, 2018 2:28 PM

Answers

  • I think that will be necessary create a new socket on server side special to receive the image, like was made here . Because that on Android not is possible send all data only in a same request, only this way:

    DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
    
    PrintWriter out = new PrintWriter(
    		new BufferedWriter(
    				new OutputStreamWriter(dos)
    		)
    );
    out.print("|PRINT|"); // First this is sent
    out.flush();
    
    dos.writeInt(array.length); // After this
    dos.write(array, 0, array.length); // And after this
    
    dos.flush();

    that turns hard the control these data on server side



    Thursday, November 1, 2018 11:34 PM

All replies

  • Hi again,

    looks like your protocol is not thought out. For example: The client sends a request to the server, the server does't know how long it is at begin. So you should read bytewise, until you know it... Btw do you know the differences between bytes and string? Are they really different in your case? You are using utf-8 on android side but you are using ascii in c#?! I told you to debug it, but you give me no informations! I can not help you like this! Debug and tell me the data you receive on you server!

    Greetings, Chris

    Tuesday, October 30, 2018 4:57 PM
  • Hi again,

    looks like your protocol is not thought out. For example: The client sends a request to the server, the server does't know how long it is at begin. So you should read bytewise, until you know it... Btw do you know the differences between bytes and string? Are they really different in your case? You are using utf-8 on android side but you are using ascii in c#?! I told you to debug it, but you give me no informations! I can not help you like this! Debug and tell me the data you receive on you server!

    Greetings, Chris

    OK, below is what is received on server C#:

    "|PRINT|" string is received and also comes the first screenshot =>

    Receiving the next screenshot (updated ) and "|PRINT|"string again, here is the begin of trouble (the same image still is displayed on server), from here the server crash =>

    Then when exit of switch...case ( server_Received() routine ), comes again to ReadCallback() routine (Listener.cs class) and starts to receive this =>

    And here was the code tested:

    Java android (Client) =>

    try {
                    dos = new DataOutputStream(clientSocket.getOutputStream());
    
                    PrintWriter out = new PrintWriter(
                            new BufferedWriter(
                                    new OutputStreamWriter(dos)
                            )
                    );
                    out.print("|PRINT|");
                    out.flush();
    
                    dos.writeInt(array.length);
                    dos.write(array, 0, array.length);
                    dos.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }

    C# (Server) =>

    void ReadCallback(IAsyncResult ar)
        {
            Info i = (Info)ar.AsyncState;
            try
            {
                int rec = i.sock.EndReceive(ar);
                if (rec != 0)
                {
                    string data = Encoding.UTF8.GetString(i.buffer, 0, rec);
                    Received(this, i, data);
                }
                else
                {
                    Disconnected(this, i);
                    return;
                }
    
                i.sock.BeginReceive(i.buffer, 0, i.buffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), i);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                Disconnected(this, i);
                i.sock.Close();
                clients.Remove(i);
            }
        }




    • Edited by FLASHCODER Wednesday, October 31, 2018 1:36 AM
    Wednesday, October 31, 2018 1:17 AM
  • Hi FLASHCODER,

    Maybe you could use a streamreader in C# and printwriter on Android to send and receive message between android and your c# server. for more information, please refer to:

    https://stackoverflow.com/questions/19465316/simple-async-android-client-with-simple-c-sharp-server

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, October 31, 2018 3:03 AM
    Moderator
  • Hi FLASHCODER,

    Maybe you could use a streamreader in C# and printwriter on Android to send and receive message between android and your c# server. for more information, please refer to:

    https://stackoverflow.com/questions/19465316/simple-async-android-client-with-simple-c-sharp-server

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thank you, but this not was usefull, the goal here is adapt this C# code above to receive text and binary data wihtout crash the application, this example linked show the same solution that is used here.
    Wednesday, October 31, 2018 2:15 PM
  • I think that will be necessary create a new socket on server side special to receive the image, like was made here . Because that on Android not is possible send all data only in a same request, only this way:

    DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
    
    PrintWriter out = new PrintWriter(
    		new BufferedWriter(
    				new OutputStreamWriter(dos)
    		)
    );
    out.print("|PRINT|"); // First this is sent
    out.flush();
    
    dos.writeInt(array.length); // After this
    dos.write(array, 0, array.length); // And after this
    
    dos.flush();

    that turns hard the control these data on server side



    Thursday, November 1, 2018 11:34 PM
  • Hi FLASHCODER,

    According to your description, it seems that you resolve the issue via creating a new socket on server, please mark it as answer, it will be beneficial to other communities who have the similar issue.

    Best regards,

    Zhanglong


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, November 5, 2018 5:13 AM
    Moderator
  • Unfortunately the "Socket" abstraction is often far too primitive for most uses and so people often end up designing layers of management code themselves. I've designed high performance client/server libraries for this kind of stuff and it isn't trivial.

    Ultimately a socket sends and receives arbitrary byte blocks and what's transmitted as one, two or three sends might end up being received as one or two or whatever received blocks, which must be reassembled to form the original sent block.

    So at the very least you need to add a "Message" abstraction so you can send and receive single messages this then makes it much easier to write the application on top of that.

    But again there are challenges because one must serialize whatever is regarded as a "message" and then deserialize it again back into a "message" but if you do this the rest of the app becomes much neater.

    Then for a host of reasons the server should ideally be fully async (even the listen) and this too isn't very hard but is pretty important if you expect high loading.

    I designed a pure .Net async messaging API which could serialize around one million messages per second (far beyond protocol buffers) it's pretty cool and robust but took quite a lot of design and engineering effort.

    Monday, November 5, 2018 7:53 PM