我试图将我的C#套接字代码复制到 Delphi 中,但是我很难从我尝试的例程中获得任何响应。
我尝试使用Indy的TIdTCPClient
组件,但直到超时才得到任何返回。
我想在 Delphi 中重新创建以下C#代码:
public static string SendXML(String xmlMessage, int cashbackAmount, bool jumpOut = false)
{
connection:
try
{
string messageToSend = xmlMessage;
String responseStringData = "";
// String responseString = "";
int headerOffset = 2;
var length = messageToSend.Length;
byte[] sendbytes = new byte[length + 2];
byte[] responsebytes = new byte[1024 * 10];
int remainder = 0;
var bytesRec = 0;
if (length < 65535)
{
sendbytes[0] = Convert.ToByte(Math.DivRem(length, 256, out remainder));
sendbytes[1] = Convert.ToByte(remainder);
}
else
{
headerOffset = 6;
sendbytes[0] = 0xFF;
sendbytes[1] = 0xFF;
byte[] intBytes = BitConverter.GetBytes(length);
Array.Reverse(intBytes);
byte[] result = intBytes;
Array.Copy(result, 0, sendbytes, 2, 4);
}
// Connect the socket to the remote endpoint. Catch any errors.
try
{
// Connect to a Remote server
// Get Host IP Address that is used to establish a connection
// In this case, we get one IP address of localhost that is IP : 127.0.0.1
// If a host has multiple addresses, you will get a list of addresses
// var connectionDetails = new Dictionary<String, String> { { "Host", HostName }, { "Port", Port.ToString() } };
// Console.WriteLine("Attempting socket connection {0}", connectionDetails);
IPHostEntry host = Dns.GetHostEntry("localhost");
IPAddress ipAddress = host.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 23001);
// Create a TCP/IP socket.
Socket s = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// Connect to Remote EndPoint
s.Connect(remoteEP);
s.ReceiveTimeout = TimeSpan.FromSeconds(30).Milliseconds;
s.SendTimeout = TimeSpan.FromSeconds(30).Milliseconds;
// Encode the data string into a byte array.
byte[] msg = Encoding.ASCII.GetBytes(messageToSend);
Array.Copy(msg, 0, sendbytes, headerOffset, msg.Length);
// Send the data through the socket.
int bytesSent = s.Send(sendbytes);
// Receive the response from the remote device.
bool isEvent = true;
bool didContainEvent = false;
bool wasCashbackEvent = false;
bytesRec = 0;
// since we are still connected, we can receive quite a few event responses and/or callbacks
// we cannot close this IP connection until all data has come back to us, else this will cause issues
// as for callbacks, they require a response, in this case we have to keep the IP connection open until
// we have replied with a callback message
while (isEvent)
{
bytesRec = s.Receive(responsebytes);
responseStringData = Encoding.ASCII.GetString(responsebytes, 0, bytesRec);
responseStringData = responseStringData.Substring(2, responseStringData.Length - 2);
responseStringData = responseStringData.Replace("\0?", "");
Console.WriteLine("Data Received = {0}", responseStringData);
Console.WriteLine();
Console.WriteLine();
isEvent = responseStringData.Contains("Esp:Event") | responseStringData.Contains("Esp:Callback");
if (isEvent)
{
if (jumpOut)
{
return responseStringData;
}
didContainEvent = true;
if (!responseStringData.Contains("Esp:Callback"))
continue;
wasCashbackEvent = true;
// Adding Cashback Callback Response Message
string cashbackMessage = $"<?xml version=\"1.0\" encoding=\"UTF-8\"?><Esp:Interface Version=\"1.0\" xmlns:Esp=\"http://www.mosaicsoftware.com/Postilion/eSocket.POS/\"><Esp:Callback TerminalId=\"TR000002\" EventId=\"DATA_REQUIRED\" ResponseData=\"{cashbackAmount}\" /></Esp:Interface>";
// Sending the cashback message with cashback amount to the callback
MessageBox.Show(responseStringData);
SendXML(cashbackMessage, 0, jumpOut: true);
// Encode the cashback data string into a byte array.
byte[] cashMsg = Encoding.ASCII.GetBytes(cashbackMessage);
Array.Copy(cashMsg, 0, sendbytes, headerOffset, cashMsg.Length);
// Send the data through the socket.
s.Send(sendbytes);
}
// Return the response
return responseStringData;
}
}
catch (Exception)
{
// txtDisplayMessage.Text = "Failed to connect to socket...";
return responseStringData;
// goto connection;
}
return responseStringData;
}
catch (Exception)
{
// txtDisplayMessage.Text = "Failed to connect to socket...";
goto connection;
// return responseStringData;
}
// string responseString = null;
// return responseStringData;
}
我的 Delphi 代码:
function TfrmEFT000.SendXML(xXMLMessage :String; xCashbackAmount :Integer = 0; wJumpOut :Boolean = False): string;
label Connection;
var
wResponseStringData :String;
wHeaderOffset, wLength, wRemainder, wByteInt :Word;
wBytesRec, wBytesSent :Integer;
wSendBytes, wResponseBytes, wMsg, wResult :TIdBytes;
wIPAddress :TIPAddress;
//wRemoteEP :PIPE_WAIT=
wHost : PHostEnt;
ipAddr : array of TIPAddress;
wIsEvent, wDidContainEvent, wWasCashbackEvent :Boolean;
begin
try
wHeaderOffset := 2;
wLength := Length(xXMLMessage);
SetLength(wSendBytes, wLength+2);
SetLength(wResponseBytes, 1024 * 10);
wRemainder := 0;
wBytesRec := 0;
DivMod(wLength, 264, wByteInt, wRemainder);
if (wLength < 65535) then
begin
wSendBytes[0] := Byte(wByteInt);
wSendBytes[1] := Byte(wRemainder);
end
else
begin
wHeaderOffset := 6;
wSendBytes[0] := $FF;
wSendBytes[1] := $FF;
// wIntBytes := BitConverter.GetBytes(wLength);
// Array.Reverse(wIntBytes);
// byte[] result = wIntBytes;
// Array.Copy(result, 0, sendbytes, 2, 4);
end;
TCPClient.IOHandler.WriteBufferOpen;
try
wMsg := IndyTextEncoding_ASCII.GetBytes(xXMLMessage);
Assert(Length(wMsg)<=Length(wSendBytes));
Move(wMsg[0], wSendBytes[2], Length(wMsg));
TCPClient.IOHandler.Write(wSendBytes,Length(wSendBytes),2);
TCPClient.IOHandler.WriteBufferClose;
// Receive the response from the remote device.
wIsEvent := True;
wDidContainEvent := False;
wWasCashbackEvent := False;
while wIsEvent do begin
Connection:
TCPClient.Socket.CheckForDataOnSource(255);
wResponseStringData := TCPClient.IOHandler.ReadLnWait(30000,IndyTextEncoding_ASCII);
if (not TCPClient.Socket.InputBufferIsEmpty) then
wResponseStringData := TCPClient.Socket.InputBufferAsString(IndyTextEncoding_ASCII);
if wResponseStringData = '' then begin
goto Connection;
end;
//TCPClient.IOHandler.ReadBytes(wResponseBytes, wBytesRec);
wResponseStringData := IndyTextEncoding_ASCII.GetString(wResponseBytes,0,wBytesRec);
wResponseStringData := wResponseStringData.Substring(2, wResponseStringData.Length - 2);
wResponseStringData := wResponseStringData.Replace('\0?', '');
LogMessage(lmtInformation, ldIn, 'Data Received = {0}');
LogMessage(lmtInformation, ldIn, wResponseStringData);
LogMessage(lmtInformation, ldIn, '');
LogMessage(lmtInformation, ldIn, '');
end;
except
on E:Exception do begin
LogMessage(lmtError, ldIn, E.Message);
ShowMessage(E.Message);
Exit;
end;
end;
except
on E:Exception do begin
TCPClient.IOHandler.WriteBufferCancel;
LogMessage(lmtError, ldIn, E.Message);
ShowMessage(E.Message);
//goto Connection;
end;
end;
end;
2条答案
按热度按时间kognpnkq1#
我发现您的 Delphi 代码存在许多问题:
wLength
始终〈65535。您不允许更大的消息。如果wLength
碰巧〉= 65535,则您根本没有将wLength
复制到wSendBytes
中,也没有将wMsg
复制到wSendBytes
的正确偏移量中。因此wSendBytes
将被损坏。wSendBytes
中的所有字节。您跳过了前2个字节。此语句:TCPClient.IOHandler.Write(wSendBytes,Length(wSendBytes),2);
需要改为:
TCPClient.IOHandler.Write(wSendBytes);
IOHandler.WriteBuffer(Open|Close|Cancel)
,因为您在它们之间只进行了1次IOHandler.Write()
调用,因此不需要缓冲数据。IOHandler.ReadLnWait()
超时的可能性,这与C#代码处理s.Receive()
超时的方式相同。此外,C#代码中甚至没有指示IOHandler.ReadLnWait()
是用于 Delphi 代码的正确方法。C#代码阅读的是任意字节,而不是基于行的文本数据。wResponseStringData
之后,但在处理它之前,您将其覆盖。首先,您使用IOHandler.ReadLnWait()
接收它,但随后您再次从IndyTextEncoding_ASCII.GetString(wResponseBytes)
接收它,尽管您已经注解掉了将字节读入wResponseBytes
的代码。因此,每次处理它时,wResponseStringData
最终都是空的。cashbackMessage
两次,每次都在不同的TCP连接上,这看起来很奇怪。但更重要的是,它没有调整sendbytes
的报头以指定cashbackMessage
的正确长度)。也就是说,我会将C#代码翻译成 Delphi +Indy,更像下面这样:
hpcdzsge2#
我设法复制了它。如果有人需要它,下面是答案。