problems sending picture from client to server - delphi

i am trying to send a picture from 'C:\picture.bmp' to 'c:\temp\picture.bmp' using server and client socket
clients onconnect event handler is as follow:
procedure TForm2.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
var
fs : tfilestream;
begin
fs := TFileStream.create('C:\picture.bmp', fmOpenRead);//picture allready exists
socket.SendStream(fs);
fs.free;
end;
and servers onclientread as :
procedure TForm2.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
fmm : tfilestream;
iLen: Integer;
Bfr: Pointer;
begin
iLen := Socket.ReceiveLength;
GetMem(Bfr, iLen);
fmm := TFileStream.Create('c:\temp\picture.bmp', fmCreate or
fmShareDenyWrite);
try
Socket.ReceiveBuf(Bfr^, iLen);
fmm.Write(Bfr^, iLen);
finally
FreeMem(Bfr);
fmm.Free;
end;
end;
picture is recieved/created but is either corrupt on was never recieved i.e created because of tfilestream.create method?
please help!what am i doing wrong?

Despite its name, SendStream() is NOT guaranteed to send the entire stream (especially if you are using a non-blocking socket). Its return value returns how many bytes are actually sent. If less than the full size of the stream are sent in one call, you have to call SendStream() again, potentially many times, to finish sending the entire stream (the same problems exists with SendText() as well).
On the other side, ReceiveLength() only reports how many bytes are available on the socket AT THAT MOMENT. That is likely to be less than the full stream being sent (likewise, ReceiveText() may not receive a full sent string either because it uses ReceiveLength() internally).
The best way to send a stream (or any arbitrary data in general) is to send the data's size first, then send the actual data afterwards. Keep calling SendBuf/Stream/Text() until that size is reached (if -1 is returned by a non-blocking socket without raising an exception, you have to wait for the socket's OnWrite event to trigger before the socket can accept more data again). On the receiving end, read the size first, then keep reading until the specified size is reached. You may have to read in multiple triggering of the OnRead event before you get all of the data.
Go to http://www.deja.com and http://forums.embarcadero.com to search the Borland/CodeGear/Embarcadero newsgroup/forum archives. I have posted example code many times before.

I don't know what's wrong, but I'd try troubleshooting a simpler problem. i.e. can you even transfer somethign simple? See if you can transfer c:\hello.txt containing just "Hello" and have it arrive in the right order. It should be easier to examine the stream and resulting file, to see if/where things are getting garbled. If you don't receive "Hello" on the server, then you know it's got nothing to do with the size or complexity of the data.

Related

TIdTCPServer OnExecute fires multiple times

I have an app that receives a TCP socket connection from another app, processes the content that is streamed across the socket, builds a request based on the received data, sends the request using TIdHTTP to an external server, processes the response, and sends an ack back to the tcp client.
procedure TMyClass.TCPExecute(AContext: TIdContext);
var
s: AnsiString;
begin
s := TransferTCPBytesToString(AContext.Connection.IOHandler);
// test string for terminating characters
if Pos(#28#13, s) > 0 then
begin
//********PROBLEM: THIS WILL BE CALLED MULTIPLE TIMES
//********WITH THE SAME COMPLETE DATA STREAM CONTENT!
BuildAndSendExternalRequest(s);
AContext.Connection.IOHandler.InputBuffer.Clear;
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler);
end;
end;
function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): AnsiString;
begin
if AnIOHandler.InputBufferIsEmpty then
Exit;
// read message from tcp client connection
AnIOHandler.ReadBytes(FTCPBytes, AnIOHandler.InputBuffer.Size, True);
SetLength(Result, Length(FTCPBytes));
// copy message bytes to string
Move(FTCPBytes[0], Result[1], Length(FTCPBytes));
end;
procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler);
var
bytes: TBytes;
begin
// FTCPBytes contains data from tcp client
// bytes will contain processed response from external server
BuildACK(FTCPBytes,bytes);
// FTCPBytes now contains ACK data
AnIOHandler.WriteDirect(FTCPBytes, Length(FTCPBytes));
end;
The request/response cycle completes successfully; the response sent back to the tcp client requestor is correct and the process executes and completes as expected.
However, because the OnExecute event fires multiple times with the same data, two requests are received, built, sent, acknowledged etc.
In looking at the logs on the tcp client app, the logs only show one request being sent. Not sure what is going on here. But I need to make sure that the request/response cycle is 1-1, not 1-many!
Thanks.
the OnExecute event fires multiple times ...
It is supposed to, as it is a looped event. The loop runs for as long as the connection is alive. The code inside your OnExecute event handler is responsible for deciding what happens on each loop iteration.
The ideal situation is where the event handler receives 1 message from the client, sends 1 response back to the client, and then exits, so that the next loop iteration is ready to handle the next request/response pair.
Which, based on your description, you are trying to do, but the code shown is not handling that very well. Most notably, the code is not accounting for the byte-stream nature of TCP. It does not differentiate one incoming message from another. It just dumps the entire IOHandler.InputBuffer content as-is into an AnsiString (why an AnsiString?), and if that AnsiString happens to contain the delimiter #28#13 then you are processing the entire AnsiString and then throwing it away, losing anything received after the delimiter (ie, part of the next message). And if the AnsiString doesn't contain the delimiter, then you are still throwing away the AnsiString, thus losing all data received so far for the current message before the delimiter arrives.
If all of your messages are delimited by #28#13, then I suggest you use the IOHandler.ReadLn() or IOHandler.WaitFor() method to read each message properly.
Also, you are re-using the same FTCPBytes member for both input and output, which in of itself is wrong, but you are also not providing any concurrent access protection for it if you have multiple client connections present.
With that said, try something more like this instead:
procedure TMyClass.TCPConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
end;
procedure TMyClass.TCPExecute(AContext: TIdContext);
var
s: String;
response: TIdBytes:
begin
s := TransferTCPBytesToString(AContext.Connection.IOHandler);
BuildAndSendExternalRequest(s, response);
// send the response back to tcp client
WriteTCPResponse(AContext.Connection.IOHandler, response);
end;
function TMyClass.TransferTCPBytesToString(AnIOHandler: TIdIOHandler): String;
begin
// read message from tcp client connection
Result := AnIOHandler.ReadLn(#28#13);
or:
Result := AnIOHandler.WaitFor(#28#13);
end;
procedure TMyClass.BuildAndSendExternalRequest(const Msg: String; var Response: TIdBytes);
begin
// send Msg to external server as needed...
// store response bytes in Response...
end;
procedure TMyClass.WriteTCPResponse(AnIOHandler: TIdIOHandler; const Response: TIdBytes);
var
bytes: TIdBytes;
begin
// Response contains processed response from external server
BuildACK(bytes, Response);
// bytes now contains ACK data
AnIOHandler.Write(bytes);
end;
... with the same data
That is not possible given the code you have shown. The call to IOHandler.ReadBytes() in TransferTCPBytesToString() is reading and discarding all bytes from the IOHandler.InputBuffer, so there is no way the same bytes can still be in that buffer when TransferTCPBytesToString() is called again at a later time. So, the only way you could be seeing the same data over and over is if the client is sending the same data over and over to begin with.

Missing data from indy TCPServer. Delphi XE2

I have an application that uses TIdTCPServer. I send null terminated messages to the server (Delphi XE2, Indy package which ships with it) sucessfully as null terminated strings.
The OnExecute procedure is as follows:
procedure TSimpleSslServerForm.TCPServerExecute(AContext: TIdContext);
var
RxBufStr: string;
begin
with AContext.Connection.IOHandler do
begin
if not InputBufferIsEmpty then
begin
RxBufStr := InputBufferAsString;
Display(RXBufStr);
lbl_EventsReceived.Caption := IntToStr(StrToInt(lbl_EventsReceived.Caption) + 1);
end;
end;
end;
My issue is that if i send alot of messages, if i do not put a 200 ms delay between messages then i loose data.
Without added delay:
I either loose data completely (13 messages received from 107 sent) and/ot the data is incorrect:
<38>Jul 10 09:37:39 cilad71 QJRN: ope=JOB WAS CHANGED date=10/07/15 time=07:59:26 sys=CILAD71 user=GCOX job=QZSOSIGN jobn= ipadr=192.168.5.121 pgm=QZSOSIGN pgmlib=QSYS date=07/10/15 time=07:59:26 user=GCOX action=PROFILE CHANGED jobname=QZSOSIGN jobnumber=189191 jobusername=QUSER jobd=QZBSJOBD ipaddress=192.168.5.121%
07/10/15 time=08:01:25 user=GCOX action=PROFILE CHANGED jobname=QZSOSIGN jobnumber=189191 jobusername=QUSER jobd=QZBSJOBD ipaddress=192.168.5.121%
<38>Jul 10 09:37:39 cilad71 QJRN: ope=JOB WAS CHANGED date=10/07/15 time=08:01:35 sys=CILAD71 user=GCOX job=QPADEV000D jobn= ipadr=192.168.5.121 pgm=QWTPIIPP pgmlib=QSYS date=07/10/15 time=08:01:35 user=GCOX action=START jobname=QPADEV000D jobnumber=189401 jobusername=GCOX jobd=QDFTJOBD ipaddress=192.168.5.121%
The event in bold is missing data.
I see the same behaviour with the TIdUDPServer component also. The only difference i see is that for UDP the required delay to receive all the data correctly is 100 ms, whereas for the TCP server a delay of less than 200 ms between messages always results in data loss.
For UDP when sending to a Windows Event service so the event appears in the corresponding Windows journal no delay is required and I see all the events correctly.
Thanks,
Geoff Cox
The TIdTCPServer code you have shown is NOT reading null-terminated messages from the socket. It is simply reading whatever raw data happens to be present on the socket at that particular moment. There might be no messages at that moment, or there might be a full message, or there might be partial pieces of multiple messages. This is just how TCP works.
If your messages are truly null-terminated, you need to read them as such, by waiting for the null terminator to arrive and then process whatever precedes it. For example, you can use the TIdIOHandler.ReadLn() or TIdIOHandler.WaitFor() method for that purpose:
procedure TSimpleSslServerForm.TCPServerExecute(AContext: TIdContext);
var
RxBufStr: string;
begin
RxBufStr := AContext.Connection.IOHandler.ReadLn(#0);
...
end;
procedure TSimpleSslServerForm.TCPServerExecute(AContext: TIdContext);
var
RxBufStr: string;
begin
RxBufStr := AContext.Connection.IOHandler.WaitFor(#0);
...
end;

Delphi 2007, Indy 10 - What is the simplest TCP/IP setup for transferring a block of data in one direction

I have an application (the "server") which updates a block of data in memory - around 100k bytes - every second.
There are 1 to 4 other instances of a "client" application running on other workstations on the same network, and these need to read the same 100k image every second.
This has been implemented up til now by writing the image to a file on the server and having the clients read from that file across the network. This has worked with no problems for many years, but lately (coincident with a move to windows 8-based hardware) it has developed a problem where the file becomes inaccessible to all nodes except one. Exiting the client application running on this node frees up the file and it then becomes accessible again to everyone.
I'm still perplexed as to the the cause of this lockout, but I'm wondering if it may be the mechanism discussed here, where a file isn't closed due to a network glitch. I'm thinking that having the clients request the data over TCP/IP would avoid this.
There doesn't need to be any handshaking other than the clients failing to connect or read data - the server just needs to go about it's business and respond to requests by grabbing the data and sending it. I'm pretty hazy however about the best architecture to achieve this. Are TidTCPClient and TidTCPServer going to cut it? I'm assuming the clients would request the data in a thread, but does this mean the server needs to run a thread continuously to respond to requests?
TIdTCPServer is a multi-threaded component. Its clients run in worker threads that it manages for you. All you have to do is implement the OnExecute event to send your data.
TIdTCPClient is not a multi-threaded component. It runs in whatever thread you use it in. So if you need to read data continuously, best to run your own worker thread to handle the reading. Indy has a TIdThreadComponent component that wraps a thread, or you can write your own TThread code manually.
100K is not a lot of data, so I would suggest simply forgetting the file altogether and allocate a buffer in memory instead. Your TIdTCPServer.OnExecute event handler can read from that buffer whenever needed. And I wouldn't even bother having the clients request data, just have the server continuously push the latest data to active clients.
Try something like this:
server:
var
Buffer: TIdBytes;
Lock: TMREWSync;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
Lock.BeginRead;
try
AContext.Connection.IOHandler.Write(Buffer);
finally
Lock.EndRead;
end;
Sleep(1000);
end;
procedure TForm1.UpdateBuffer;
begin
Lock.BeginWrite;
try
// update the Buffer content as needed...
finally
Lock.EndWrite;
end;
end;
initialization
Lock := TMREWSync.Create;
SetLength(Buffer, 1024 * 100);
finalization
SetLength(Buffer, 0);
Lock.Free;
client:
procedure TForm1.IdThreadComponent1Run(Sender: TIdThreadComponent);
var
Buffer: TIdBytes;
begin
IdTCPClient1.IOHandler.ReadBytes(Buffer, 1024 * 100);
// use Buffer as needed...
end;
procedure TForm1.Connect;
begin
IdTCPClient1.Connect;
try
IdThreadComponent1.Start;
except
IdTCPClient1.Disconnect;
raise;
end;
end;
procedure TForm1.Disconnect;
begin
IdTCPClient1.Disconnect;
IdThreadComponent1.Stop;
end;

Cant seem to recive a reply vir com port

Im trying to send a command to a dev. board and then receive a reply
E.G
(me) set attrib=yes
(Dev. Board) O.K
or
(Dev. Board) E.R.R
But it doesn't bounce back anything ... not an O.K or an E.R.R
while the board is booting echo is on .. so if I send the commands while the board is booting it
it will bounce back an 'set attrib=yes' and once booted an 'E.R.R' because you cant send commands while booting.
my best guess is that it isn't reading the reply in time or trying to read it too soon.
procedure TReaderProgrammer.Button2Click(Sender: TObject);
Var
Buffer: AnsiString;
RxData: string;
Count : integer;
begin
if ComPort1.Connected then
begin
ComPort1.WriteStr(edtSendText.Text+#13);
comport1.ReadStr(RxData,Count);
Buffer := Buffer + Rxdata;
memoRxData.Text := memoRxData.Text+Buffer+#13+#10;
end;
end;
Here are several open questions in the air, so I have to make some assumptions that might be wrong, but let's see.
I don't know what comm port library you are using, so I'm assuming it is the CPort library from SourceForge. I have never used it myself, but I have read that it is made Unicode aware, such that you can call the write methods with a unicodestring parameter which will be converted by the library to ansistring before sending. Similarily when receiving ansistring from the outer world, the library will convert to unicodestring for the Read methods.
Due to the asynchronous nature of serial communication, it is important to understand that when you send something using the write method, the method returns immediately while the library and OS spit out the characters one at at time at a pace defined by the baud rate. As a result your first code never received anything, because you were already attempting to read from the comm port before the external device even received the first character.
It is good to see that you have now taken the first step to success by implementing an event handler for (presumably library event) OnRxChar.
The OnRxChar probably fires for each character (or couple of characters). You need to have a buffer that is persistent between these events. A local var (as you have it now and which is allocated on the stack) in the event handler is not persistent, it is lost every time the event handler exits.
You should declare the Buffer variable as a field of TReaderProgrammer. I don't know why you defined the buffer to be AnsiString, but I suggest you try with string (ref discussion above regarding Unicode awareness).
type
TReaderProgrammer = class
..
RxBuffer: string;
..
end;
The buffer needs to be cleared when you send a new command to the external device in order for it to be ready to receive new data as a response to your command.
EDIT: Alternatively you can clear the RxBuffer immediately when you have received and processed a full response.
The TReaderProgrammer.ComPort1RxChar should look like this sofar:
procedure TReaderProgrammer.ComPort1RxChar(Sender: TObject; Count: Integer);
var
RxData : string;
begin
(Sender as TComPort).ReadStr(RxData,Count);
RxBuffer := RxBuffer + Rxdata;
...
end;
The rest of the event handler is, I guess, probably just to see progress of reception, so nothing more about that.

Handling TUdpSocket

I'm trying to use the TUdpSocket in Delphi. What I want to do is connect to a UDP server, send some data and wait for answer. Data is sent correctly, but the control does not receive anything. I don't know why. I've been struggling with this problem for many hours now and I'm going to give up :-(.
I tried to use TIdUDPClient, but the situation is the same. Data is sent properly, but none is received.
Only does TIdUDPServer work more or less properly for it both sends and receives data. Unfortunately data reception is handled by a separate thread (main or other, depending on the ThreadedEvent property) which forces me to use synchronization and complicate the whole code. I would like to handle UDP connection in my own thread. Just send some data and call WaitForData() to wait for an answer and then handle it in the same thread.
And if opossible, I don't want to use any third party controls, but if it is the only solution, I accept it.
Thank you very, very much for your help in advance.
---- Examples ---
i) TUDPSocket:
var
lR, lW, lE: boolean;
begin
UdpSocket1.LocalPort := '1600';
UdpSocket1.RemotePort := '1600';
UdpSocket1.RemoteHost := '127.0.0.1';
UdpSocket1.Connect;
UdpSocket1.Sendln('test');
UdpSocket1.Select(#lR, #lW, #lE, 2000);
if lR then
ShowMessage(UdpSocket1.Receiveln());
end;
As you can see, the control should receive the data it transmits. And apparently it does, for the lR evaluates to true after the Select() method is called. But Receiveln() returns an empty string, as ReceiveBuf() does. When i start a UDP server and send some data to it, it is received properly, so I'm certain that the data is really sent.
You should need nothing more than this:
function SayHi(Host: String; Port: Integer): String;
var
Client: TIdUDPClient;
begin
Client := TIdUDPClient.Create(nil);
try
Client.Host := Host;
Client.Port := Port;
Client.Send('Hello');
Result := Client.ReceiveString(1000);
finally
Client.Free;
end;
end;
which will, in the same thread, send a UDP packet and either receive a UDP packet or raise an exception after a timeout.
If the above doesn't work, check (using something like Wireshark that the client's actually sending the data. Then check on the server that it's actually receiving the packet.
Send() ultimately (on Windows) calls WinSock's sendto(), and ReceiveString() uses select() and recvfrom(). You could sort've simulate a TIdUDPServer by
while not Terminated do begin
S := Client.ReceiveString(1000);
DoSomething(S);
end;
Another possibility would be to use Synapse for your communication. It is a simple communications library which performs blocking calls, so works well in a single worker thread where you want to stop and wait for data rather than rely on an event to fire. The latest versions in SVN have support for the latest versions of Delphi.

Resources