Indy's default text encoding is ASCII (because the majority of Internet protocols are still largely ASCII based, unless they define extra extensions to support Unicode). That is why you are getting ?
for non-ASCII characters. To send non-ASCII characters, you need to tell Indy which text encoding to use that is compatible with the characters you are exchanging. UTF-8 is usually the best choice for that. There are three ways you can do that:
set the global
GIdDefaultTextEncoding
variable in theIdGlobal
unit. It is set toencASCII
by default, you can set it toencUTF8
instead:procedure TForm1.FormCreate(Sender: TObject); begin GIdDefaultTextEncoding := encUTF8; end;
set the
TIdIOHandler.DefStringEncoding
property toTIdTextEncoding.UTF8
(orIndyTextEncoding_UTF8
if you are using Indy 10.6+):procedure TForm1.IdTCPClient1Connected(Sender: TObject); begin IdTCPClient1.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8; // or: // IdTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; end;
pass
TIdTextEncoding.UTF8
(orIndyTextEncoding_UTF8
) directly to theAByteEncoding
parameter ofWriteLn()
:IdTCPClient1.IOHandler.WriteLn(..., TIdTextEncoding.UTF8); // or: // IdTCPClient1.IOHandler.WriteLn(..., IndyTextEncoding_UTF8);
Keep in mind that you are using an Ansi version of Delphi, where string
maps to AnsiString
, and thus Indy has to perform an additional Ansi-to-Unicode conversion of AnsiString data before it can then apply the specified text encoding to produce the bytes it transmits. Typically, Indy uses the OS's default Ansi encoding to handle that initial conversion (so if your AnsiString
data is Greek encoded, and your OS is set to Greek, you will be fine), however you can use the TIdIOHandler.DefAnsiEncoding
property, or the ASrcEncoding
parameter of WriteLn()
, if you need to specify that your AnsiString
data is using a different encoding.
As for your socket error, without seeing a call stack leading up to the error, or at least which line of your code is raising it, that is difficult to troubleshoot. My guess is that it is related to you calling ReadLn()
inside of the finally
block regardless of whether WriteLn()
or Write()
actually succeeded. That code needs to be moved out of the finally
block, it does not belong there.
Try something more like this instead:
procedure TForm1.LoadFileButtonClick(Sender: TObject);
begin
OpenDialog1.Filter := 'All Files (*.*)';
OpenDialog1.FilterIndex := 1;
if OpenDialog1.Execute then
begin
Edit1.Text := ExtractFileName(OpenDialog1.FileName);
Edit3.Text := OpenDialog1.FileName;
// Indy has its own FileSizeByName() function...
Edit2.Text := IntToStr(FileSizeByName(OpenDialog1.FileName));
end;
end;
procedure TForm1.SendFileButtonClick(Sender: TObject);
var
IncommingText: string;
Strm: TFileStream;
begin
if not CheckBox1.Checked then
begin
ShowMessage('Please connect to the Server');
Exit;
end;
if OpenDialog1.FileName = '' then
begin
ShowMessage('Please choose a file');
Exit;
end;
Strm := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);
try
IdTCPClient1.IOHandler.WriteLn(Edit1.Text + '@' + Edit2.Text + ';' + Edit3.Text + ',', TIdTextEncoding.UTF8);
IdTCPClient1.IOHandler.LargeStream := True;
IdTCPClient1.IOHandler.Write(Strm, 0 , True);
finally
Strm.Free;
end;
Memo1.Lines.Add('File Sent');
IncommingText := IdTCPClient1.IOHandler.ReadLn;
if IncommingText = 'DONE!' then begin
Memo1.Lines.Add('File ' + Edit1.Text + ' ' + Edit2.Text + ' was received successfully by the Server');
//APPLICATION.ProcessMessages;
end else
begin
Memo1.Lines.Add('File ' + Edit1.Text + ' was not received by the Server');
end;
end;
Lastly, just an FYI, you are setting the AWriteByteCount
parameter of Write()
to True, so it is going to transmit the stream size (as an Int64
because of LargeStream=True
) before then sending the TStream
data, so putting the file size in the WriteLn()
data is redundant.