Search code examples
c#delphiuwppascalindy10

Networking between Indy10 and System.Net.Sockets


Me and my partner working on a project where my partner is developing in Pascal and using Indy10 socket for networking and I'm developing under C# in an UWP project. Therefore for networking I have to use streamsocket namespace.

The problem is, that my partner uses ReadStream method with -1 and false parameters, that mean that the length of the receiving stream is indetermined. Therefore, I have to send a length prefix calculated (in this case I use BitConverter.GetBytes method) from the data in my stream (for example using DataWriter class) continued with the data itself. But Indy10 socket don't receives nothing. It looks like it stucks on waiting for the packet or I don't know.

Does anybody hit that problem? Please help, we've been looking after the solution for about two weeks. I had read all the similar questions here in StackOverflow, a blog about the use of framing, delimitering. Searched for solution in Indy Socket source code itself. Please help!

UPDATE 1
C# code. In this case I am sending from a WPF application. The logic, how streams are used in UWP are almost the same, just using StreamSocket and DataWriter.

TcpClient client = new TcpClient(hostTextBox.Text, int.Parse(portTextBox.Text));

Byte[] byteMessage = Encoding.Unicode.GetBytes(messageTextBox.Text);
Byte[] lenghtPrefix = BitConverter.GetBytes(byteMessage.Length);
Byte[] concatedData = new byte[lenghtPrefix.Length + byteMessage.Length];
lenghtPrefix.CopyTo(concatedData, 0);
byteMessage.CopyTo(concatedData, lenghtPrefix.Length);

NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(concatedData);
writer.Flush();

stream.Close();
client.Close();

But we have tried this solution too:

Byte[] byteMessage = Encoding.Unicode.GetBytes(messageTextBox.Text);
Byte[] lenghtPrefix = BitConverter.GetBytes(byteMessage.Length);

Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Connect(hostTextBox.Text, int.Parse(portTextBox.Text));

socket.Send(lenghtPrefix, SocketFlags.None);
socket.SendBufferSize = byteMessage.Length;
socket.Send(byteMessage, SocketFlags.None);

socket.Close();

Pascal code on the server side, waiting for stream. It is crucial to receive in this way, because the software's other modules (also written in Pascal using Indy10) communicating with the server in this way.

function Receive(const AContext: TIdContext): string;
var
  Stream: TMemoryStream;
begin
  try
    Result := '';
    Stream := TMemoryStream.Create;
    if  AContext.Connection.Connected then
      AContext.Connection.IOHandler.ReadStream(Stream, -1, False);
  finally
    Result := UnPackStream(Stream);
    Stream.Free;
  end;
end;

Solution

  • Finally, here is the solution. We worked on that all day. We used Synapse socket as a reference. In the end here is the code under UWP, and we tested it in other desktop projects (WPF, Winforms) too (with TcpClient and NetworkStream). BitConverter and prefixing the hole stream with a number didn't worked, because Indy waits for the length in binary format, therefore the byte array have to contain values representing the binary length. I hope it will help others having similar problem!

    Windows.Networking.Sockets.StreamSocket socket = new Windows.Networking.Sockets.StreamSocket();
    Windows.Networking.HostName serverHost = new Windows.Networking.HostName(hostTextBox.Text);
    string serverPort = portTextBox.Text;
    await socket.ConnectAsync(serverHost, serverPort);
    
    //Write data to the server.
    DataWriter writer = new DataWriter(socket.OutputStream);
    
    byte[] data = Encoding.Unicode.GetBytes(messageTextBox.Text);
    byte[] size = new byte[4];
    
    ushort x = (ushort)(data.Length >> 16);
    ushort y = (ushort)(data.Length);
    size[0] = (byte)(x / 256);
    size[1] = (byte)(x % 256);
    size[2] = (byte)(y / 256);
    size[3] = (byte)(y % 256);
    
    writer.WriteBytes(size);
    writer.WriteBytes(data);
    await writer.StoreAsync();
    await writer.FlushAsync();
    
    writer.DetachStream();
    
    //Read response.
    var bytes = new byte[4];
    
    DataReader reader = new DataReader(socket.InputStream);
    await reader.LoadAsync(4);
    
    reader.ReadBytes(bytes);
    int length = bytes[0] * 256 * 256 * 256 + bytes[1] * 256 * 256 + bytes[2] * 256 + bytes[3];
    
    byte[] dat = new byte[length];
    var count = await reader.LoadAsync((uint)length);
    reader.ReadBytes(dat);
    
    responseTextBlock.Text = Encoding.Unicode.GetString(dat);
    
    reader.DetachStream();
    socket.Dispose();