Indy10和System.Net.Sockets之间的网络连接

本文关键字:网络 连接 之间 Sockets System Net Indy10 | 更新日期: 2023-09-27 17:54:53

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

问题是,我的伙伴使用ReadStream方法与-1和false参数,这意味着接收流的长度是不确定的。因此,我必须发送一个长度前缀计算(在这种情况下,我使用BitConverter。GetBytes方法)从流中的数据(例如使用DataWriter类)继续处理数据本身。但是Indy10插座没有接收到任何东西。好像卡在等待数据包上了,或者我不知道。

有人遇到这个问题吗?请帮忙,这个解决方案我们已经找了两个星期了。我在StackOverflow上看到了所有类似的问题,这是一个关于框架、分隔使用的博客。在印地套接字源代码本身搜索解决方案。请帮助!

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

Indy10和System.Net.Sockets之间的网络连接

最后,这是解决方案。我们为此工作了一整天。我们使用Synapse socket作为参考。最后,这里是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();

在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主要是一个小端环境,但ReadStream()期望以大端为单位的整数字节。

试试这样写:

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