Synapse abort timeout lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Jake012345 · 2020-05-31 11:11 Hello! I want to abort a timeout when I know if I don't need the listening, but I don't want ot close the connection. How can i? There is some command? ---------------------------------------------------------------------- rvk · 2020-05-31 15:28 You can't abort a timeout. Once given, the connection will wait until that timeout. You can choose a smaller timeout and build a loop around the communication. In that case you can jump out the loop when you want or when multiple number of smaller timeouts have occurred. ---------------------------------------------------------------------- Jake012345 · 2020-05-31 16:20 Okay. BTW I could make my application without freezing, just with Application.ProcessMessages is 'blcksock' unit. That's the final repair, because when the transfer done, the application freezing for timeout interval. ---------------------------------------------------------------------- rvk · 2020-05-31 16:29 Quote from: Jake012345 on May 31, 2020, 06:20:43 pmI could make my application without freezing, just with Application.ProcessMessages is 'blcksock' unit. That's the final repair, because when the transfer done, the application freezing for timeout interval. After recvstreamraw it should just continue, not freeze. So I'm but sure what's in your code that does the freezing. And I don't think you can put processmessages in blcksock. Synapse is a blocking library so during transport your calling thread (which might be your main thread) will freeze. The only solution to that would be to implement communication in separate threads. ---------------------------------------------------------------------- Jake012345 · 2020-05-31 16:50 do I submit the project? ---------------------------------------------------------------------- rvk · 2020-05-31 16:58 Quote from: Jake012345 on May 31, 2020, 06:50:23 pmdo I submit the project? In lazarus you can choose project>publish Give a clean directory and publish. You can zip that directory and attach it to a post. ---------------------------------------------------------------------- Jake012345 · 2020-05-31 18:41 Thanks! that's useful. ---------------------------------------------------------------------- Jake012345 · 2020-05-31 18:43 OR ---------------------------------------------------------------------- rvk · 2020-05-31 20:07 Ok. I had a look at the non-modified version. It seems the RecvStreamRaw() version always waits until the timeout to know that it has the end of the file. It might be better to use RecvStream() (of RecvStreamIndy). With RecvStream(), the size of the file is first transferred and the client knows exactly how much data it should receive. So there is no need for the timeout. So change Connection.SendStreamRaw to Connection.SendStream and Connection.RecvStreamRaw to Connection.RecvStream After that, you don't have a freeze anymore. Next... if the target file already exists I would add " (1)" to the filename. If that already exists I would add " (2)" etc. And finally the Unable to create file "". Somehow... after the filetransfer is done... the GetTimerTimer is triggered again while mode is still fileaccept. That triggers a second TFileStream.Create on an empty filename (which gives the error). I tried to put a GetTimer.Enabled := false; at the top and a GetTimer.Enabled := true; at the bottom of the timer event but that doesn't work. Although I definitely would add those lines anyway. For now you could make the line this:   if (mode='fileaccept') and (infile_name <> '') then begin In that case at least it won't try to create the file Anyway... The second project with the modified blcksock.pas isn't needed. I also noticed you had a loop around RecvStreamRaw in that modified project. That's also not correct because RecvStreamRaw should receive the entire stream and you don't want a loop there. And the loop on FileSize() isn't even useful because the stream might not have had the time to write it's buffer to the file so the filesize if only definite when .free is called. Anyway... I would rake your first project and see if you can figure out why the second trigger on the timer is called with infile_name=''. Edit: I think I found out why the GetTimerTimer gives the unable to create "" error. You send on the server side a string with Connection.SendString('>StartSendFile' But DIRECTLY afterwards you send the stream. It's better to build in a delay before sending so the client has time to set up the receiving end. (That's why I in the beginning suggested a separate socket for filetransfer might be better) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Sleep(1000);MainStream := TFileStream.Create(OpenFileDialog.FileName, fmOpenRead);Connection.SendStream(MainStream);MainStream.Free; This should be much better. O, one final note... During transfer your application does freeze. But that's because the library is 'blocking'. You could hack the synapse code. But if you really want to fix that you'll need threads. ---------------------------------------------------------------------- Jake012345 · 2020-06-01 11:14 Okay. I had some errors when I tried build in, the treading so I want to test it, in a new app. But what is the most simple way to overload one thread? ---------------------------------------------------------------------- rvk · 2020-06-01 11:45 Quote from: Jake012345 on June 01, 2020, 01:14:08 pmBut what is the most simple way to overload one thread? What do you mean by 'overload one thread'. You can begin to move the sending and receiving of files to a separate thread. But in that case I would also implement a separate socket/port for the binary raw data. Because you can't use a socket in two thrwads at the same time. And in that case you also separate string from binary. On sending side you can implement a small loop to wait for a new connection (you can do this in the main thread). When the connection is established, move the socket to a thread to do the sendstream. On the receiving end you can directly establish the connection and move it to a thread for recvstream. There is a simple example for tcp communication and threads somewhere in the wiki or forum (can't look right now, about to head out for some golf 🌞). ---------------------------------------------------------------------- Jake012345 · 2020-06-01 13:26 I did it, but I have Access Violation and SIGSEGV errors. EDIT: I forgot to create the threads. But now it works like without threads... ---------------------------------------------------------------------- rvk · 2020-06-01 20:58 I take it the version attached to your post is where you didn't create the connectionthread. There are some more remarks. *) you can't call logging directly in your thread. It might work now but tmemo isn't thread-safe and you can't write directly to a variable (so tmemo) from the mainthread. You must use synchronize for that. If you do it like you have now you can have unexpected freezes. *) you have the filesocket.free now in the thread. In that case I would also create it in the execute (or create) or the thread. *) you have a lot of global variabele. You actually should have no global variabele whatsoever but they should all be moved somewhere else. It does work now but it will only become more complicated. For now it should work fine (seeing the code without executing it) but I would move some things around (which I can't go into right now typing on a small mobile but especially those global variables). For example, while one file is transfering you should be able to keep typing text and even start a second file-transfer. That doesn't seem possible now. (For multiple filetransfers you could create a tlistbox (aligned at bottom) of current file transfers and show a progressbar besides them) But that all depends on how sophisticated you want to made it. ---------------------------------------------------------------------- Jake012345 · 2020-06-02 11:33 I think now it shoud work, but I got RunError (Unable to create file) in the accept file section in TCustomThread.Execute procedure. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  MainStream:=TFileStream.Create(infile_name,fmCreate); And sometimes SIGSEGV unable to open file error in the sender. code ---------------------------------------------------------------------- rvk · 2020-06-02 11:44 Quote from: Jake012345 on June 02, 2020, 01:33:53 pmI think now it shoud work, but I got RunError (Unable to create file)Your infile_name is probably empty. You should pass the infile_name to the create function of the thread. There you should put it in a local thread variable and then you can use that in the execute procedure. Also... the error in your picture is because you have a strtoint somewhere, where the string is empty (and you can't convert an empty string into a integer). Show the complete code of the thread again and the snippet of how you create and call the thread. (I can make the create constructor of the thread if you can't) ---------------------------------------------------------------------- Jake012345 · 2020-06-02 11:51 Here's the code. I tested and the infile_name is not empty. (I put a ShowMwssage(infile_name) before the stream creation in execute procedure) ---------------------------------------------------------------------- Jake012345 · 2020-06-02 11:56 I figured out, probably the program trys 2 times to create the file ---------------------------------------------------------------------- Jake012345 · 2020-06-02 12:01 when I cleared the .Execure command form timer, It works fine. !?!? But the sender isn't good ---------------------------------------------------------------------- Jake012345 · 2020-06-02 12:04 I deleted it form everywhere and it works!!!!! My program is good! So I Don't need this command? Just if I want to call it after start again? ---------------------------------------------------------------------- Jake012345 · 2020-06-02 12:09 I'm very grateful to you rvk! Thanks! and sure thansk for all guys who helped me! ---------------------------------------------------------------------- Jake012345 · 2020-06-02 12:42 I can't believe... A new failure. The accepted file size is not equal with the source. And the acceping side is can't finish, because of that. But the sender is write 'I'm done' and don't send any data. ---------------------------------------------------------------------- rvk · 2020-06-02 13:01 A few remarks about you last posted synctransfer.zip *) In TCustomThread.Execute you have a loop around Connection.SendStream() and Connection.RecvStream(). Remove both loops. SendStream and RecvStream shouldn't be used in a loop. If you do... the same file gets transferred multiple times. SendStream and RecvStream itself already have loops internally to SEND THE ENTIRE STREAM. So there shouldn't be any loop around them *) Because you now have a separate socket for filetransfer you can remove the Sleep(2000) from the FileSendButtonClick(). The sender can directly go into the thread and wait there for an incoming connection. *) In the Thread.Execute you access Form1.OpenFileDialog.FileName directly. But form1 isn't thread safe and you may not access it directly from other threads. You could use synchronize to get the value but it's easier to populate an outfile_name thread-variable (the same way you did with the infile_name). *) The Application.ProcessMessages; in TCustomThread.ShowStat probably isn't necessary because the main-thread is always running. So ProcessMessages is done there anyway. (If it does give you trouble you can put it in again). Your latest failure about the size not being equal is probably because of those loops around Send and RecvStream. Because the streams are send and received multiple times. So if you fix point 1, that will go away too. EDIT... Woops... still one big problem. You're still using the global variable Connection for filetransfer. But TTCPBlockSocket is also not thread safe. So you shouldn't use it mixed in the main and your transfer thread. I thought you had a version where you had a separate filesocket? That one creates a separate socket for the filetransfer. You can only remove the Sleep(2000) when you have separate sockets for filetransfer. ---------------------------------------------------------------------- Jake012345 · 2020-06-02 16:02 I think you wrong. The simple send/recv stream do not have loop. But now I can use the ~streamRaw() because it not block the all program. And now it works fine! I'll do the progress with a timer. And I have 2 questions: 1. Can I use more threads?>I't require 1 core/thread>Can I run a multhreaded app. on a 1 core PC? 2.Can I declare a TTimer in other class? and use it like the visual timer? ---------------------------------------------------------------------- rvk · 2020-06-02 16:20 Quote from: Jake012345 on June 02, 2020, 06:02:42 pmI think you wrong. The simple send/recv stream do not have loop. No, I'm right. Put your cursor on SendStream( and press Alt+up arrow. Then press Ctrl+Shift+Down arrow You see it says InternalSendStream() Again Alt+Up arrow and Ctrl+Shift+Down arrow and you are in the InternalSendStream. You see a loop there so that EVERYTHING in the stream is send. The WithSize of the function call is true (so size of stream is send first). After that you have a repeat loop with until yr <= 0 and yr is the result of Stream.read() which will be 0 if the complete stream is read. SO NEVER EVER put a loop around SendStream because then you might have the problem that the file is transferred multiple times. (especially if the data isn't written to disk yet) The loop is absolutely not needed and can cause serious trouble if not everything is directly written to the disk. Quote from: Jake012345 on June 02, 2020, 06:02:42 pmBut now I can use the ~streamRaw() because it not block the all program. No, don't use ~streamRar(). ~stream() is exactly the same. The upside of ~stream is that the size of the stream is send first. In that case the client knows how much data to receive (and doesn't have to wait timeout seconds for the last data). Your problem with ~streamRar was that it always waits the given timeout to see if the stream is stopped. ~stream() is the better alternative (without the loop around it). Quote from: Jake012345 on June 02, 2020, 06:02:42 pm1. Can I use more threads?>I't require 1 core/thread>Can I run a multhreaded app. on a 1 core PC? If you make sure you don't use global variables you can use multiple threads. It does not require 1 core/thread. If there are less cores, the threads are divided over the available cores. You then don't have the speed gain of more cores but you do have the flexibility. Quote from: Jake012345 on June 02, 2020, 06:02:42 pm2.Can I declare a TTimer in other class? and use it like the visual timer? That depends how you declare and use it. You also had a version with a FileSocket. I think that is a better choice than sending the file over the string/message-socket. But ultimately that's your own choice. ---------------------------------------------------------------------- Jake012345 · 2020-06-02 17:54 Okay, but If I don't need the timeout in ~stream(), why there is here? Or It has a condition like if timeout>presetted_timeout OR wholeFileReceived then stop_the_listening; ---------------------------------------------------------------------- rvk · 2020-06-02 19:08 Quote from: Jake012345 on June 02, 2020, 07:54:51 pmOkay, but If I don't need the timeout in ~stream(), why there is here? For SendStream the internal Timeout is used for checking if every packet is send correctly. For RecvStream the given Timeout is used for every packet that get received. So don't set that Timeout too small. (You can see this if you follow RecvStream with the keystrokes I mentioned earlier. You end up in RecvStreamSize and you can see it there) In that RecvStreamSize routine you also see "for n := 1 to (Size div FSendMaxChunk) do" That is the loop for Size (divided by the buffer size). That's the number of buffer reads the client does. That's also why you shouldn't put RecvStream into a loop. So a Timeout for RecvStream of 10ms might be a bit small. Because every read should easily be faster but one packet might accidentally be delayed. In that case you don't want a Timeout of 10ms. Make the Timeout 1000ms for example. Normal packages are read a lot faster so the Timeout isn't really needed, but if there is some hickup then the 1000ms should be enough. That's also the reason why I suggested using the FileSocket and not doing the transfer over the String-socket. Because if you directly run RecvStream() after receiving the sting for filetransfer, the sender might not be ready yet. And if you then have a Timeout of 10ms on the client, it directly times out. When doing a separate FileSocket... the client needs to connect to the sender. And that gives both the time to set up a correct connection (over which only the stream is send). Alternative, like you have it now, you should give the RecvStream a Timeout of at least 2000ms maybe 4000ms, because the sender needs to start the SendStream(). The 4000ms doesn't matter much for after the transfer because the receiver knows the size of the stream so doesn't need to wait after the filetransfer to see if there is more. ---------------------------------------------------------------------- Jake012345 · 2020-06-03 07:45 Ok I'm done! Now I want to add the reconnect, continue procedure. How Can I do it? And how can I test it? I did something, but I can't test because I can't disturb the connection... ---------------------------------------------------------------------- Jake012345 · 2020-06-03 07:52 Okay I installed VBox and I can turn off the net But what is the best method to reconnect and stop the thread until reconnect and continue after that? ---------------------------------------------------------------------- rvk · 2020-06-03 08:13 Quote from: Jake012345 on June 03, 2020, 09:52:58 amBut what is the best method to reconnect and stop the thread until reconnect and continue after that? You can't pause and resume a thread. You could simulate it with a critical section like shown here https://forum.lazarus.freepascal.org/index.php?topic=25362.0 But because SendStream is one call, you can't interrupt it in the loop and you would need to build your own SendStream and RecvStream where you take into account that the thread is paused. And even then... when the connection is lost, you can't just continue afterwards without reconnecting. So, building in a pause and continue (like with torrents) is really really hard. Even when you can continue the download, you would need to take into account that the last block of bytes aren't send correctly (and maybe written as 0,0,0,0,0 to the file). And the sender should know how much the client has received, and how much data was correctly received (so you can't use filesize on the clientside). ---------------------------------------------------------------------- Jake012345 · 2020-06-03 08:21 and the thread.sleep? ---------------------------------------------------------------------- Jake012345 · 2020-06-03 08:24 timer interval=10 if canread(10)=false then begin   thread.Sleep(10);  socket.connect(ip,port); end; ---------------------------------------------------------------------- rvk · 2020-06-03 08:34 You could try but it won't fix the problem that the sender doesn't know how much of the data is really arrived correctly at the receiver. Especially when the connection was cut. ---------------------------------------------------------------------- Jake012345 · 2020-06-03 08:45 Yeah... Than how can I send large files in a bad environment? ---------------------------------------------------------------------- rvk · 2020-06-03 08:49 Quote from: Jake012345 on June 03, 2020, 10:45:21 amThan how can I send large files in a bad environment? Divide the transfer by blocks and send each block individually with a checksum. At the end put them together and do a final checksum. Something like how torrents work. You didn't specify this when you started all this. Not making specifications before programming makes it all a lot harder. ---------------------------------------------------------------------- Jake012345 · 2020-06-03 09:12 ---------------------------------------------------------------------- Jake012345 · 2020-06-03 09:25 Now I got a fault. The last stream didn't sended. Can you help me? ---------------------------------------------------------------------- Jake012345 · 2020-06-03 09:39 Oh no! I solved it. The Thread.execute>filesend> I changed the Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if MainStream.size>=FileUtil.FileSize(Form1.OpenFileDialog.FileName) then... to Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       if MainStream.position>=FileUtil.FileSize(Form1.OpenFileDialog.FileName) then ---------------------------------------------------------------------- rvk · 2020-06-03 09:45 You can remove the lines 117 and 135 if MainStream.Position>=FileUtil.FileSize(Form1.OpenFileDialog.FileName) and if MainStream.Size>=StrToInt(infile_size) At the moment they don't do anything. They even prevent MainStream.Free which will result in leaks when the filetransfer is interrupted. You would only need those lines when you want to try to resume, but that's not (yet) build in. Or are you planning to build in a loop in case of connection interruption? (and then resending the entire file?) You can remove one of the Sleep(2000) in the program (but one should remain because you still use the string-socket). But it's not needed to have them both. ---------------------------------------------------------------------- Jake012345 · 2020-06-03 09:50 On the receiving I need it because without it the last stream appears on Log and ins't in file ---------------------------------------------------------------------- rvk · 2020-06-03 09:56 Quote from: Jake012345 on June 03, 2020, 11:50:31 amOn the receiving I need it because without it the last stream appears on Log and ins't in file That shouldn't happen. Look at the code. Thread.Execute is always executed only once (and you don't have a loop in there). So: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       MainStream:=TFileStream.Create(infile_name,fmCreate);       Connection.RecvStream(MainStream,60000);       if MainStream.Size>=StrToInt(infile_size) then begin        MainStream.free;        StatString:='>:Transfer Done!';        Synchronize(@ShowStat);        mode:='none';        Form1.ProgressLabelPercent.Caption:='100%';        Form1.ProgressBar.Position:=Form1.ProgressBar.Max;        Form1.ProgressLabelByte.Caption:=(infile_size+'/'+infile_size);      end; In case of an interrupt the MainStream.Free isn't executed and you get a leak. In case of a successful RecvStream(), then MainStream.Size should ALWAYS equal infile_size so the if should always be true. If you really want to take connection interruption into account, then do this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       if MainStream.Size = StrToInt(infile_size) then begin // EXACTLY THE SAME SIZE        StatString:='>:Transfer Done!';        Synchronize(@ShowStat);        mode:='none';        Form1.ProgressLabelPercent.Caption:='100%';        Form1.ProgressBar.Position:=Form1.ProgressBar.Max;        Form1.ProgressLabelByte.Caption:=(infile_size+'/'+infile_size);      end      else begin        StatString:='>:TRANSFER FAILED!';        Synchronize(@ShowStat);        mode:='none';      end;      MainStream.free;That way at least the MainStream is freed in both cases (and you get a neat message about the failure). (also notice that the received size needs to be the exact size of infile_size. And not smaller or bigger.) ---------------------------------------------------------------------- rvk · 2020-06-03 11:26 In addition to my previous post... You could set mode to "request_resend" when the filetransfer fails on the receiving side. The receiving side can then request a resend from the sender via the normal messages. Also... you access the string mode and the Form1 directly in the thread. This is normally really dangerous and can lead to unexplained freezing of your program. You should only access those via syncronise. But because you only access them once... (during start and after end), maybe you can get away with it. But keep it in mind. But you can move those Form1.ProgressLabelPercent etc. to the ShowStat anyway. In that case they are executed in Synchronize and thus in the main thread. Then the only dangerous thing is the access to the mode string. But that is minimal. ---------------------------------------------------------------------- Jake012345 · 2020-06-03 13:08 wait! Wich is the main thread? What is start on program start. But now the second thread can run commands in main thread? And an other 'question': I created the thread and started it with .start and the .execute procedure auto started. Why? Because it is in the 'protected' section? Or the .execute is a default procedure? ---------------------------------------------------------------------- rvk · 2020-06-03 13:52 Quote from: Jake012345 on June 03, 2020, 03:08:46 pmWich is the main thread?Main thread is the thread in which your main program itself runs. QuoteWhat is start on program start.All processes, even the main program, run in threads. So the main thread is the one where you have your form1 etc. QuoteBut now the second thread can run commands in main thread?Yes, but you must never access anything that's thread-unsafe without using synchronize. And about the whole LCL (VCL on Delphi) is thread-unsafe. Even simple strings are thread-unsafe. So directly accessing them could lead to problems if multiple thread access that memory space at the same time. QuoteI created the thread and started it with .start and the .execute procedure auto started. Why? Because it is in the 'protected' section? Or the .execute is a default procedure? Execute is the default 'run' procedure of a thread. You can read about it here https://wiki.lazarus.freepascal.org/Multith readed_Application_Tutorial#The_TThread_Class ---------------------------------------------------------------------- Jake012345 · 2020-06-04 07:41 But there is still some problem. The failed transfers has 1/3 rate is on same computer. I think that't not too normal. ---------------------------------------------------------------------- rvk · 2020-06-04 07:44 Quote from: Jake012345 on June 04, 2020, 09:41:55 amThe failed transfers has 1/3 rate is on same computer. I think that't not too normal. On the same computer you should have no failed transfers. Code? ---------------------------------------------------------------------- Jake012345 · 2020-06-04 08:51 Sorry... ---------------------------------------------------------------------- rvk · 2020-06-04 09:13 Quote from: Jake012345 on June 04, 2020, 09:41:55 amThe failed transfers has 1/3 rate is on same computer. I think that't not too normal. I can think of only two things why the transfers would fail. Here, the transfer-rate is about 2 failed on 15 good transfers. You still have a TTimer in the main thread which accesses FileThread.MainStream. It's the same way around... a thread may not access unsafe variables from the main thread... but at the same time, the main thread may not access unsafe variables from any other thread. So the access in GetTimerTimer() is a problem. (You can access FileThread.infile_size etc before the thread is really started. But not FileThread.MainStream, while that stream is used in the thread !!!) Another potential problem is that you use the Connection variable (Socket) in the thread. This Socket is created in the main thread (and used for string-messages). So using this thread-unsafe variable also in a thread can lead to problems. That's why in the beginning suggested you create a separate file-transfer socket. For this you could add a parameter to your >StartSendFile for the port the server is listening on. >StartSendFile,filename.txt,1234,4324243,3246 (where 3246 would be the port the server is listening on for connection of a new socket) In that case you can also eliminate the two Sleep(2000) and the transfer will start much prompter. I have one question. This program is only used on an internal network? Or do you need to forward ports later on to make it accessible to the internet??? If it's local you can choose a random free port for filetransfer on the server and pass that on to the client. In that case, multiple files can be transferred at the same time from multiple clients. If it's an internet access program, you might want to choose a static port. You now have 3245 for string-socket... you could use 3246 for file transfer. That will limit your potential for multiple transfers at the same time though. But for this case you wouldn't need to pass the port to the client because it's always fixed at 3246. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 09:16 It created for use on LAN and online too. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 09:18 But how can I Show stat? With the synchronize(@showstat) I can run the commands in the main thread, so it's safe? ---------------------------------------------------------------------- Jake012345 · 2020-06-04 09:20 The stat must shown every (1 sec or more often). And the thread is 'frozen' when do the ~stream() so I don't know ---------------------------------------------------------------------- Jake012345 · 2020-06-04 10:38 for some reason I can't send more files, in one run of program (I have to restart the whole program to send other file). And I can't figure out what is the problem. But I made the additional socket for FileThread. ---------------------------------------------------------------------- rvk · 2020-06-04 10:48 Quote from: Jake012345 on June 04, 2020, 11:18:23 amBut how can I Show stat? With the synchronize(@showstat) I can run the commands in the main thread, so it's safe? Yes, that is safe. But the Synchronize(@ShowStat) is only executed after the Send/RecvStream. And not during. You also have Form1.ProgressBar.Position:=FileThread.MainStream.Size; in the GetTimerTimer(). And that is a big no-no because FileThread.MainStream.Size is constantly updated in the thread. If you strip out the progress for a moment, do you transfers all succeed after that ?? ---------------------------------------------------------------------- Jake012345 · 2020-06-04 10:51 I solved it. But If I use the ShowProgrees procedure with loop, in synchronize, the main thread wont be frozen? ---------------------------------------------------------------------- rvk · 2020-06-04 10:58 Quote from: Jake012345 on June 04, 2020, 12:51:04 pmBut If I use the ShowProgrees procedure with loop, in synchronize, the main thread wont be frozen? How and when are you doing this? You only called Synchronize in the thread itself AFTER the RecvStream. And that should be too late to show the progress. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 11:03 I put it before that, but now the program freezing and doesn't go to the next command because the synchronize(@ShowProgress) don't stop until the transfer done, what now can't be ---------------------------------------------------------------------- Jake012345 · 2020-06-04 11:04 Like 'Kill myself in the past with time travelling' ---------------------------------------------------------------------- rvk · 2020-06-04 11:13 Quote from: Jake012345 on June 04, 2020, 01:03:36 pmI put it before that, but now the program freezing and doesn't go to the next command because the synchronize(@ShowProgress) don't stop until the transfer done, what now can't be Hihi. Yes, that's what I thought. When using RecvStream and SendStream you can't show a progress within the thread itself because it's in the ~stream() function. The only solution would be to chop up ~stream and do the loop for sendbuffer etc. yourself, or use the callback in the socket. Look at the OnStatus event for the TTCPBlockSocket. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FileConnection := TTCPBlockSocket.Create;FileConnection.OnStatus := MyStatusHook; // this is a local procedure in the thread which does synchronize(@ShowProgress); ---------------------------------------------------------------------- TRon · 2020-06-04 11:23 @rvk I am far from knowledgeable on synapse but i just had a look at the OnMonitor hook. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  {:This procedural type is used for monitoring of communication.}  THookMonitor = procedure(Sender: TObject; Writing: Boolean;    const Buffer: TMemory; Len: Integer) of object; Could that perhaps be used to keep an eye on progress ? I have no idea when HR_ReadCount / HR_WriteCount starts counting :-) (i assume it is global just as OnMonitor parameters?) ---------------------------------------------------------------------- rvk · 2020-06-04 11:24 Quote from: TRon on June 04, 2020, 01:23:19 pmI am far from knowledgeable on synapse but i just had a look at the OnMonitor hook. I think the TTCPBlockSocket.OnStatus (like I mentioned in my last post) is easier for this. Edit: O, you are correct. OnMonitor will work simular. OnStatus will give you also the status and connection messages besides the receive and send bytes. OnMonitor will only give you the byte-transfers (for SendBuffer and RecvBuffer). For Jake012345: You can just use the MainStream.Position still for your progress. You don't need the parameters of the OnMonitor() itself. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FileConnection := TTCPBlockSocket.Create;FileConnection.OnMonitor := MyMonitorHook; // this is a local procedure in the thread which does synchronize(@ShowProgress); ---------------------------------------------------------------------- Jake012345 · 2020-06-04 11:33 And how can I create hook? ---------------------------------------------------------------------- rvk · 2020-06-04 11:36 Quote from: Jake012345 on June 04, 2020, 01:33:28 pmAnd how can I create hook? Just by adding this line. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FileConnection := TTCPBlockSocket.Create;FileConnection.OnMonitor := MyMonitorHook; and Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TCustomThread = class(TThread)    //...  public    //...    procedure MyMonitorHook(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer);    //...  end; procedure MyMonitorHook(Sender: TObject; Writing: Boolean; const Buffer: TMemory; Len: Integer);begin  Synchronize(@ShowProgress);end; BTW, This could slow down the sending process because for every buffer read and write the syncrhonize is executed. If that is really the case (slowdown) you could build in a counter to only execute the Synchronize in the MonitorHook about 1 time out of a 100 or so (or other value). But first try it like this. ---------------------------------------------------------------------- TRon · 2020-06-04 11:40 @rvk Glad i could be of help  . On that subject...  Quote    {:This event is good for calling your code during long socket operations.       (Example, for refresing UI if class in not called within the thread.)       Rate of heartbeats can be modified by @link(HeartbeatRate) property.}     property OnHeartbeat: THookHeartbeat read FOnHeartbeat write FOnHeartbeat; and Quote  {:This procedural type is used for hook OnHeartbeat. By this hook you can    call your code repeately during long socket operations.    You must enable heartbeats by @Link(HeartbeatRate) property!}   THookHeartbeat = procedure(Sender: TObject) of object; If you do not require the parameters that the monitor hook is providing... ? ---------------------------------------------------------------------- rvk · 2020-06-04 11:47 Quote from: TRon on June 04, 2020, 01:40:20 pmOn that subject...  Quote    {:This event is good for calling your code during long socket operations.       (Example, for refresing UI if class in not called within the thread.)       Rate of heartbeats can be modified by @link(HeartbeatRate) property.}     property OnHeartbeat: THookHeartbeat read FOnHeartbeat write FOnHeartbeat; If you do not require the parameters that the monitor hook is providing... ? Ah, yes. That is also a possibility. You would need to provide a specific HeartbeatRate because OnHeartbeat could be executed a lot of times if you don't provide it. OnStatus, OnMonitor and OnHeartbeat are all executed differently and also a different number of times. So it would take some investigating which one would be best for what purpose. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 11:48 unit1.pas(69,78) Error: Identifier not found "TMemory" ---------------------------------------------------------------------- rvk · 2020-06-04 11:49 Quote from: Jake012345 on June 04, 2020, 01:48:37 pmunit1.pas(69,78) Error: Identifier not found "TMemory" Code? ---------------------------------------------------------------------- Jake012345 · 2020-06-04 11:50 Code. ---------------------------------------------------------------------- rvk · 2020-06-04 11:53 Quote from: Jake012345 on June 04, 2020, 01:50:59 pmCode. You can change TMemory to pointer. And add the TCustomThread before the hookprocedure. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FileConnection := TTCPBlockSocket.Create;FileConnection.OnMonitor := @MyMonitorHook; // don't forget the @ Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TCustomThread = class(TThread)    //...  public    //...    procedure MyMonitorHook(Sender: TObject; Writing: Boolean; const Buffer: pointer; Len: Integer);    //...  end; procedure TCustomThread.MyMonitorHook(Sender: TObject; Writing: Boolean; const Buffer: pointer; Len: Integer);begin  Synchronize(@ShowProgress);end; ---------------------------------------------------------------------- TRon · 2020-06-04 11:56 Quote from: Jake012345 on June 04, 2020, 01:48:37 pmunit1.pas(69,78) Error: Identifier not found "TMemory" Besides solution given by rvk, you can add unit synsock to your uses clause, it is defined there. ---------------------------------------------------------------------- TRon · 2020-06-04 11:59 Quote from: rvk on June 04, 2020, 01:47:08 pmAh, yes. That is also a possibility. You would need to provide a specific HeartbeatRate because OnHeartbeat could be executed a lot of times if you don't provide it. OnStatus, OnMonitor and OnHeartbeat are all executed differently and also a different number of times. So it would take some investigating which one would be best for what purpose. I suspected as such, that is why i was more or less guessing (hoping for a more definite answer from your experience) as i have no idea what would be best for this situation. I am still busy on other things before i am able to return more seriously to synapse. When i do, i'll try to make some use cases to see how some of these events turn out. Thank you for your input. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 12:20 Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer); What means the parameters? EDIT: That's worked and really got slow. ---------------------------------------------------------------------- rvk · 2020-06-04 12:21 Quote from: Jake012345 on June 04, 2020, 02:20:25 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer); What means the parameters? Writing is if you are sending or receiving. Buffer is the pointer to the buffer about to be transfered. Len is the length of that buffer. All parameters are really not needed if you just want to know the end (Stream.position) of your stream to show a progressbar. ---------------------------------------------------------------------- Jake012345 · 2020-06-04 19:03 Okay. That's worked. But QuoteIf that is really the case (slowdown) you could build in a counter to only execute the Synchronize in the MonitorHook about 1 time out of a 100 or so (or other value). But first try it like this. I can't do that. My technic doesn't work. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer); var i:integer=0;begin  i:=i+1;  if i=ProgressRefreshRate then begin  Synchronize(@ShowProgress);  i:=0;  end;end; ---------------------------------------------------------------------- rvk · 2020-06-04 19:08 Quote from: Jake012345 on June 04, 2020, 09:03:57 pmI can't do that. My technic doesn't work. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer); var i:integer=0;begin  i:=i+1;  if i=ProgressRefreshRate then begin  Synchronize(@ShowProgress);  i:=0;  end;end;  You have i as local variable and it gets initialized to 0 each time the procedure is run. Make it a class variable, setting it to 0 at the top of execute, and it should work. ---------------------------------------------------------------------- lucamar · 2020-06-04 19:38 Quote from: Jake012345 on June 04, 2020, 09:03:57 pmI can't do that. My technic doesn't work. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer); var i:integer=0;begin  i:=i+1;  if i=ProgressRefreshRate then begin  Synchronize(@ShowProgress);  i:=0;  end;end; Note that, as it is, your code is completely equivalent to this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);begin  if 1 = ProgressRefreshRate then    Synchronize(@ShowProgress);end; Your code would work if you did it like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);const  i:integer = 0;begin  i := i+1;  if i = ProgressRefreshRate then begin    Synchronize(@ShowProgress);  i:=0;  end;end; But that would only ever trigger the if just once, unless you change ProgressRefreshRate elsewhere before this is called. If it's indeed a "rate" (that is, if you mean the if condition to be true, say, each 10 interations) you should do it, for example, like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);const  i:integer = 0;begin  i := i+1;  if (i mod ProgressRefreshRate) = 0 then begin    Synchronize(@ShowProgress);    if i >= 100 then i:=0;  end;end; ---------------------------------------------------------------------- Jake012345 · 2020-06-05 02:27 I wrote this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);begin  inc(ProgressRefreshCooldown,1);  if ProgressRefreshCooldown>=ProgressRefreshRate then begin  Synchronize(@ShowProgress);  ProgressRefreshCooldown:=0;  end;end;     And I think thats good. But I have a qusetion: how fast it repeats? millisecons or faster? ---------------------------------------------------------------------- Jake012345 · 2020-06-05 02:52 And Why is that send the message twice if I click on the button although the Enter on edit is works fine? ---------------------------------------------------------------------- rvk · 2020-06-05 07:41 Quote from: Jake012345 on June 05, 2020, 04:27:49 amI wrote this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);begin  inc(ProgressRefreshCooldown,1);  if ProgressRefreshCooldown>=ProgressRefreshRate then begin  Synchronize(@ShowProgress);  ProgressRefreshCooldown:=0;  end;end;     And I think thats good. But I have a qusetion: how fast it repeats? millisecons or faster? It might be better to check GetTickCount64 instead of relying on a counter. With GetTickCount64 you can specify the exact timespan on which you want to execute the ShowProgres. Something like Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.MonitorHook(Sender: TObject; Writing: boolean; const Buffer: Pointer; Len: Integer);begin  if GetTockCount64>=NextTickCountCheck then  begin    Synchronize(@ShowProgress);    NextTickCountCheck:=GetTickCount64 + 500; // 500ms but could also be 1000ms depending on what you want  end;end; ---------------------------------------------------------------------- rvk · 2020-06-05 08:01 Quote from: Jake012345 on June 05, 2020, 04:52:30 amAnd Why is that send the message twice if I click on the button although the Enter on edit is works fine? That has nothing to do with the communication. If you set a Log.Lines.Add('a') at the top of the buttonclick you will see it is executed twice. I could also not figure out why until I removed the button and placed a new button (with other name). It then gave me an error on Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.MessageEditEditingDone(Sender: TObject);begin  SendMessageButton.Click;end;       There you Simulate the SendMessageButton.Click. But THAT event is also triggered when you click the button AND when you are done editing. So you end up with two buttonclicks (and two sends). The TEdit is smart enhough not to trigger MessageEditEditingDone() when you didn't change anything in the TEdit. That's why the second time you do a click, only one message gets send. So make a separate procedure SendMessage and call then individual from both MessageEditEditingDone and SendMessageButtonClick. BTW, Don't use LineEnding but use CRLF in this case. Synapse really expects CRLF and LineEnding is platform specific. ---------------------------------------------------------------------- Jake012345 · 2020-06-05 08:11 Thanks. But I'll do it with: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   if connected then begin    if MessageEdit.Text<>'' then    Connection.SendString(MessageEdit.Text+LineEnding);   MessagEdit.Text:='';  end; ---------------------------------------------------------------------- rvk · 2020-06-05 08:20 Quote from: Jake012345 on June 05, 2020, 10:11:42 amThanks. But I'll do it with: That's also possible. But the Buttonclick still gets executed twice (which isn't really what should happen). Setting the TEdit to '' just so the send doesn't happen on the second ButtonClick is kind of a 'hack' ---------------------------------------------------------------------- Jake012345 · 2020-06-05 10:14 Okay. I think, I'm done with my App. Here is: Link (The file was too large) ---------------------------------------------------------------------- rvk · 2020-06-09 11:52 Quote from: Jake012345 on June 05, 2020, 12:14:47 pm(The file was too large) You can usually omit the .res file. The publish function also doesn't copy that file. I have some small suggestions (if I may). *) The Sleep(2000) in the Execute isn't needed anymore. Because the server has a separate socket, and will listen (and wait) until that socket is connected, the sleep can be removed. *) Also... the repeat until loop is not needed. The Listen function waits until the connection is made. You can do one CanRead on that socket to make sure the connection is there. So no need for the loop. *) You had a final Form1.Progress = 100% in the Execute. It would be easier (and safer) to call ShowProgress at the end of the sending and receiving. Because the pointers ALWAYS are on the end of the file, the 100% progress is automatically reported. *) One extra thing. Code is easier to read if you do proper code indentation. You can do that automatically. In Package > Install Package you can install jcfidelazarus 2.0. If installed (and IDE restarted) you can press Ctrl+D to automatically indent your code correctly. The only setting I usually change is Tools > Options > JCF Format Settings > Clarify > Line Breaking to Never. (I like to break my lines manually if needed) You get something like this. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCustomThread.Execute;begin  if connected then  begin    if mode = 'filesend' then    begin      FileServer := TTCPBlockSocket.Create;    FileServer.CreateSocket;      FileServer.Bind(Form1.IpEdit.Text, IntToStr(StrToInt(Form1.PortEdit.Text) + 1));      FileServer.Listen; // will wait for connection      if FileServer.CanRead(60000) then // is connection made?      begin        FileConnection := TTCPBlockSocket.Create;        FileConnection.OnMonitor := @MonitorHook;        FileConnection.CreateSocket;        FileConnection.Socket := FileServer.Accept;        MainStream := TFileStream.Create(Form1.OpenFileDialog.FileName, fmOpenRead);        FileConnection.SendStream(MainStream);        Synchronize(@ShowProgress);        StatString := '>:Transfer Done!';      Synchronize(@ShowStat);      end;    end;    if mode = 'fileaccept' then    begin      FileConnection := TTCPBlockSocket.Create;      FileConnection.OnMonitor := @MonitorHook;      FileConnection.CreateSocket;      FileConnection.Connect(Form1.IpEdit.Text, IntToStr(StrToInt(Form1.PortEdit.Text) + 1));      if FileConnection.LastError = 0 then      begin        MainStream := TFileStream.Create(infile_name, fmCreate);        FileConnection.RecvStream(MainStream, 60000);        Synchronize(@ShowProgress);        if MainStream.Size = StrToInt(infile_size) then          StatString := '>:Transfer Done!'      else          StatString := '>:Transfer FAILED!';        Synchronize(@ShowStat);      end;    end;    mode := 'none';    MainStream.Free;    FileServer.Free;    FileConnection.Free;  end;end;