【问题标题】:Networking between Indy10 and System.Net.SocketsIndy10 和 System.Net.Sockets 之间的网络连接
【发布时间】:2016-07-07 04:07:45
【问题描述】:

我和我的合作伙伴正在开展一个项目,我的合作伙伴正在使用 Pascal 进行开发并使用 Indy10 套接字进行网络,而我正在使用 C# 在 UWP 项目中进行开发。因此,对于网络,我必须使用 streamsocket 命名空间。

问题是,我的伙伴使用带有 -1 和 false 参数的 ReadStream 方法,这意味着接收流的长度是不确定的。因此,我必须从我的流中的数据(例如使用 DataWriter 类)继续发送数据本身计算的长度前缀(在这种情况下我使用 BitConverter.GetBytes 方法)。 但是 Indy10 套接字什么也没有收到。看起来它一直在等待数据包,或者我不知道。

有人遇到过这个问题吗?请帮忙,我们已经解决了大约两周的问题。我在 StackOverflow 中阅读了所有类似的问题,这是一篇关于使用框架、分隔符的博客。在 Indy Socket 源代码本身中搜索解决方案。 请帮忙!

更新 1
C# 代码。在这种情况下,我从 WPF 应用程序发送。在 UWP 中使用流的逻辑几乎是一样的,只是使用了 StreamSocket 和 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();

但我们也尝试过这个解决方案:

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 代码,等待流。以这种方式接收至关重要,因为软件的其他模块(也是使用 Indy10 用 Pascal 编写的)以这种方式与服务器通信。

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;

【问题讨论】:

  • 尝试用 Wireshark 嗅探数据包,看看你发送不正确或他接收不正确。
  • 我们无法调试我们看不到的代码,因为对代码的作用和行为方式的描述有些模糊。
  • 你试过GitHub上的官方StreamSocket sample吗?此示例包含接收和发送功能。并且在发送数据的同时,还将内容的长度写为 UINT32 值在内容之前。您可以使用此示例来确定问题出在客户端还是服务器端。

标签: c# delphi uwp pascal indy10


【解决方案1】:

最后,这是解决方案。我们整天都在努力。我们使用 Synapse 套接字作为参考。最后是 UWP 下的代码,我们也在其他桌面项目(WPF、Winforms)中对其进行了测试(使用TcpClientNetworkStream)。 BitConverter 并在洞流前加上数字前缀不起作用,因为 Indy 等待二进制格式的长度,因此字节数组必须包含表示二进制长度的值。 希望对遇到类似问题的人有所帮助!

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();

【讨论】:

    【解决方案2】:

    在 Indy 中调用 TIdIOHandler.ReadStream() 时,如果 AByteCount 为 -1 且 AReadUntilDisconnect 为 False,ReadStream() 将读取前 4 个字节 (Int32) 或 8 个字节 (Int64)(取决于TIdIOHandler.LargeStream 属性)并将它们解释为网络字节顺序(大端)的整数,然后读取该整数指定的字节数(如果大于 0)。

    您的第一个 C# 示例的主要问题是您使用的是 StreamWriter,它是为发送文本而不是二进制数据而设计的。如果您尝试使用StreamWriter 编写Byte[] 数组,它将不会按原样发送,它将被格式化为与Indy 不兼容的文本表示。您不需要StreamWriter,您可以按原样使用NetworkStream,它有一个Write() 方法用于Byte[] 数据。或者使用BinaryWriter

    这两个 C# 示例的另一个问题是您使用 BitConverter 时没有考虑字节序。它的GetBytes(Int32) 方法以与调用机器的体系结构相同的字节序创建一个字节数组。 Windows 主要是 little-endian 环境,但 ReadStream() 期望使用 big-endian 的整数字节。

    试试这样的:

    TcpClient client = new TcpClient(hostTextBox.Text, int.Parse(portTextBox.Text));
    
    Byte[] byteMessage = Encoding.Unicode.GetBytes(messageTextBox.Text);
    Byte[] lengthPrefix = BitConverter.GetBytes(byteMessage.Length);
    if (BitConverter.IsLittleEndian)
         Array.Reverse(lengthPrefix);
    
    NetworkStream stream = client.GetStream();
    stream.Write(lengthPrefix, 0, lengthPrefix.Length);
    stream.Write(byteMessage, 0, byteMessage.Length);
    
    stream.Close();
    client.Close();
    

    或者这个:

    TcpClient client = new TcpClient(hostTextBox.Text, int.Parse(portTextBox.Text));
    
    Byte[] byteMessage = Encoding.Unicode.GetBytes(messageTextBox.Text);
    Byte[] lengthPrefix = BitConverter.GetBytes(byteMessage.Length);
    if (BitConverter.IsLittleEndian)
         Array.Reverse(lengthPrefix);
    
    NetworkStream stream = client.GetStream();
    BinaryWriter writer = new BinaryWriter(stream);
    
    // BinaryWriter.Write(Int32) writes in little-endian only,
    // so you still need to use Write(Byte[]) for the length value...
    writer.Write(lengthPrefix); 
    writer.Write(byteMessage)
    writer.Flush();
    
    stream.Close();
    client.Close();
    

    【讨论】:

    • 你说得对,使用 StreamWriter 有时会发送混乱的数据。无论如何,我没有尝试反转长度前缀数组。我会试一试。非常感谢您的帮助!
    猜你喜欢
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    • 1970-01-01
    相关资源
    最近更新 更多