Fazi Fazi - 3 months ago 17
C# Question

C# Transferring multiple data types over a single port (receiving)

The Problem



The other day I had an "amazing" idea for my C# Application. Instead of using one port for each data type (text, image, file), I would like to use a single port for all three data types. I am very close to implementing this.

So far I've got a TcpListener listening on port 23722. When a TcpClient connects I start writing incoming data to a file using
StreamWriter.Write(datareceived);
and data comes in a specific pattern (each line represents an image, a text message or a file):


dnett{This is a sample text message.)
dneti{NLtqmuvtGBdDY546 ... NLtqmuvtGBdDY546}
dnetf{Example.exe,NLtqmuvtGBdDY546 ... NLtqmuvtGBdDY546}


As you can see from above images and files are converted to Base64 before sending. To convert them back I would use
byte[] tmp = Convert.FromBase64String(string)
, but the problem is that I can't read the file line by line while the data is still incoming (being written with StreamWriter). The other problem is, that I don't know which data has already been processed (example: which files had already been written to the filesystem).




What I need is:




  • a solution for reading/writing file at the same time

  • knowing which data has already been processed



OR


  • another way of doing this (different approach)






Thanks



By the way I am only 15 yrs old and English is not my first language, so I am sorry for potentially stupid question and for mistakes in the above question asked.

Answer

Usually, you would first send the size of the "message" and then the actual "message" (from the server). You could extend this to first send the size of your message type, then send your message type, then send the size of the actual message and then send that message.

To read back, you'd do something like this

using System;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using Newtonsoft.Json;
using Unity3DRemoteRendererClient.Helpers;

namespace Unity3DRemoteRendererClient.Communications.TCPCommunication {
 public sealed partial class TCPClientManager {
  public sealed class Receiver {
   private NetworkStream _stream;
   private Thread _thread;

   //Subscribe to this event if you want an event ever time data arrives.
   public event EventHandler < UnityToClientMessage > DataReceivedEvent;
   private static ManualResetEvent ShutDownEvent = new ManualResetEvent(false);

   public void Start(NetworkStream stream) {
    _stream = stream;
    _thread = new Thread(Run);
    _thread.Start();
   }

   private void Run() {
    try {
     // ShutdownEvent is a ManualResetEvent signaled by
     // Client when its time to close the socket.
     while (!ShutDownEvent.WaitOne(0)) {
      try {
       if (!_stream.DataAvailable) continue;

       //Read the first 4 bytes which represent the size of the message, and convert from byte array to int32
       var sizeinfo = new byte[4];
       _stream.Read(sizeinfo, 0, 4);
       var messageSize = BitConverter.ToInt32(sizeinfo, 0);

       //create a new buffer for the data to be read
       var buffer = new byte[messageSize];

       var read = 0;
       //Continue reading from the stream until we have read all bytes @messageSize
       while (read != messageSize) {
        read += _stream.Read(buffer, read, buffer.Length - read);
       }

       //I use flatbuffers, so you should deserialize yourself.
       var message = new UnityToClientMessage().FlatDeserialize(buffer);

       //raise data received event
       OnDataReceived(message);

      } catch (IOException ex) {
       // Handle the exception...
       throw;
      }
     }
    } catch (Exception ex) {
     // Handle the exception...
     throw;
    } finally {
     _stream.Close();
    }
   }

   private void OnDataReceived(UnityToClientMessage e) {
    EventHandler < UnityToClientMessage > handler = DataReceivedEvent;
    if (handler != null) {
     handler(this, e);
    }
   }

   public void ShutDown() {
    ShutDownEvent.Set();
   }

  }
 }
}

Just modify it to first read the size of the message type message and then to read the actual message. You can do a switch statement once you have the message describing the type of message coming, and process it accordingly.