Page 2 of 2
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 8:31 pm
by Oso
infratec wrote: Thu Oct 20, 2022 7:39 pm
No, I mean you don't need to handle: #PB_NetworkEvent_Connect #PB_NetworkEvent_Disconnect
Thanks infratec, I since read about it being "connectionless", as it says in the docs. Actually, the way I need to do this is for the "calling" routine to send data to the "called" routine, and for the "called" routine to send data back. Do I need to open a UDP server at both ends with different port numbers? It's already working as a "called" prog receiving data and proving it by putting it into a file (both below). It's a background process that doesn't need a console display.
Code: Select all
; ** Mainprog.pb - Main calling routine
OpenConsole()
If RunProgram("d:\udpprog\subprog.exe", "", "", #PB_Program_Open )
ConnectionID = OpenNetworkConnection("127.0.0.1", 24 , #PB_Network_UDP, 5000 )
Repeat
Print("Please enter a string to send to the background process 'subprog.pb' or q to quit : ")
str$ = Input()
SendNetworkString(ConnectionID, str$, #PB_Unicode);
If str$="q"
End
EndIf
ForEver
EndIf
Code: Select all
; ** d:\udpprog\Subprog.pb - Sub prog routine
*Buffer = AllocateMemory(1000)
If CreateNetworkServer(0, 24, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
Repeat
ServerEvent = NetworkServerEvent()
If ServerEvent
ClientID = EventClient()
Select ServerEvent
Case #PB_NetworkEvent_Data
result.i = ReceiveNetworkData(ClientID, *Buffer, 1000)
strinp$=Left(PeekS(*Buffer, -1, #PB_Unicode),result.i)
output.s + strinp$
If Left(strinp$,1) = "q"
CreateFile(0,"d:\udpprog\output.txt")
WriteString(0,output.s)
CloseFile(0)
quit = 1
EndIf
EndSelect
EndIf
Until Quit = 1
EndIf
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 9:06 pm
by Olli
Oso wrote:Apologies also for the apparent double-post
Absolutely no problem !
I know this light bug which sometimes occurs.
And I think, when I will switch my computer on, I will test and give back the example of infratec. I shut several network tasks down since I bought it, but W10 seems to be the king (vs older versions) of the local network pipes.
Also, infratec mentionned a freeed CPU, so I have an old word in the head : DMA. And
Fred removed the several identifying numbers of patents I published on the french forum by his removing of the french off topic later, as it was not a work to be shared (names of the 3 scientists, dates of studying, dates of publishing, associated societies). I published it, 5 years ago, when my dad was paralysed. It was 3 patents about network, DMA, and harddrive :
1) harddrive/network DMA channel
2) network/external server DMA channel
3) distant DMA control
CPU absolutely freeed !
So, I imagine if I wanted to start a debate against infratec, considering the classical process pipes would be faster than network i/o : I would be directly dead !!
Network is as fast as video now, so it is a very interesting subject.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 10:06 pm
by infratec
Code: Select all
; ** d:\udpprog\Subprog.pb - Sub prog routine
If CountProgramParameters() = 1
Port = Val(ProgramParameter(0))
*Buffer = AllocateMemory(1000)
If CreateNetworkServer(0, Port, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
ConnectionID = OpenNetworkConnection("127.0.0.1", Port + 1, #PB_Network_UDP, 1000)
Repeat
If NetworkServerEvent() = #PB_NetworkEvent_Data
result.i = ReceiveNetworkData(EventClient(), *Buffer, 1000)
If result
strinp$ = PeekS(*Buffer, result, #PB_UTF8|#PB_ByteLength)
SendNetworkString(ConnectionID, "Ok")
output.s + strinp$ + #CRLF$
Debug "Received: " + strinp$
If Left(strinp$,1) = "q"
CreateFile(0, "output.txt")
WriteString(0, output.s)
CloseFile(0)
quit = 1
EndIf
EndIf
Else
Delay(10)
EndIf
Until Quit = 1
CloseNetworkServer(0)
EndIf
EndIf
Code: Select all
; ** Mainprog.pb - Main calling routine
OpenConsole()
Prog = RunProgram("subprog.exe", "5000", "", #PB_Program_Open)
If Prog
ConnectionID = OpenNetworkConnection("127.0.0.1", 5000 , #PB_Network_UDP)
*Buffer = AllocateMemory(2048)
Server = CreateNetworkServer(#PB_Any, 5001, #PB_Network_UDP, "127.0.0.1")
Print("Please enter a string to send to the background process 'subprog.pb' or q to quit : ")
Repeat
Key$ = Inkey()
If Key$ = #CR$
Debug "Send: " + str$
SendNetworkString(ConnectionID, str$)
If str$ = "q"
ExitSoon = #True
EndIf
str$ = ""
Else
str$ + Key$
EndIf
If NetworkServerEvent(Server) = #PB_NetworkEvent_Data
Length = ReceiveNetworkData(EventClient(), *Buffer, MemorySize(*Buffer))
If Length
Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)
Debug "Received: " + Received$
If ExitSoon
Exit = #True
EndIf
EndIf
EndIf
Until Exit
FreeMemory(*Buffer)
CloseNetworkServer(Server)
CloseNetworkConnection(ConnectionID)
CloseProgram(Prog)
EndIf
CloseConsole()
Without threads, the main loop needs to run continiously.
So It was necesarry to replace Input() by Inkey().
Due to less time, I did not use EnableExplizit.
You should definately use it always in every program.
It saves you time. Trust me.
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 9:27 am
by Oso
infratec wrote: Thu Oct 20, 2022 10:06 pm
Without threads, the main loop needs to run continiously. So It was necesarry to replace Input() by Inkey().
Many thanks indeed Infratec, I appreciate all you've done for me here. Yes, I fully understand why you included the thread - without the thread for listening for received data, we have to deal with local keyboad input and received data inside the same loop. To be honest, I agree that threading is the answer, but I haven't become proficient with it yet. I don't really follow the syntax well - it seems a bit arcane to me, but I'm sure I'll get there in the end
infratec wrote: Thu Oct 20, 2022 10:06 pm
Due to less time, I did not use EnableExplizit. You should definately use it always in every program. It saves you time. Trust me.
Yes, agreed, I know exactly what you mean. I use EnableExplicit in programmes that I intend to keep and put into production, but not when testing. I prefer to keep test code as short as possible.
I've been testing the main prog and sub prog and you've implemented it as I thought would be necessary - i.e. an IP port for the main prog and a port for the sub-prog. I've also just extended it to call two sub-progs at the same time and that works too, although I think I need to refine the below slightly. I don't think I'm necessarily receiving the data back from the specific server, because EventClient() isn't being specific, but I'll work on this further.
Code: Select all
If NetworkServerEvent(Server1) = #PB_NetworkEvent_Data
PrintN("Received something from subprog 1")
Length = ReceiveNetworkData(EventClient(), *Buffer, MemorySize(*Buffer))
If Length
Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)
PrintN("Received back from subprog 1 : " + Received$)
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 11:28 am
by mk-soft
Before working with networking, one should learn the basics:
- ISO model, Protocols (IP, UDT, TCP, etc). Link:
https://en.wikipedia.org/wiki/OSI_model
- SendNetworkData passes the data to the send buffer. Not that they have been sent
- ReceiveNetworkData fetches the receive buffer. Not separated according to sent data, but the data accumulated up to that point.
- There are separate send and receive buffers for each connection. Distinguishable with EventClient(), EventServer()
- It is your task to ensure that the data is complete. For example, with a header or with start and end characters.
- With TCP it is guaranteed that the data up to 64kB are in the correct order in the receive buffer. Not the completeness.
Example with UDP as short text sending ..
Link:
viewtopic.php?t=74200
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 5:55 pm
by Oso
infratec wrote: Thu Oct 20, 2022 10:06 pm
Code: Select all
; ** d:\udpprog\Subprog.pb - Sub prog routine
[...]
ConnectionID = OpenNetworkConnection("127.0.0.1", Port + 1, #PB_Network_UDP, 1000)
A quick question Infratec... this threw me a bit at first. I noticed that when I tried to change the case of "ConnectionID" to "connectionid", PB changed it back again. This lead me to find out that ConnectionID is actually a PB function. In fact I thought at first that it was a function, because of the capitalisation

I see now that you prefer capitalised variable names.
Just out of interest, I put a debug in my code and ConnectionID(connectionid) returned 188 in both concurrent instances of the running code. Just curious what the PB ConnectionID() function is for? The help doesn't explain much. It just says "Returns the system identifier".
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 5:56 pm
by Oso
mk-soft wrote: Fri Oct 21, 2022 11:28 am
Before working with networking, one should learn the basics:
Thanks for the information mk-soft

Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 6:00 pm
by Oso
Olli wrote: Thu Oct 20, 2022 9:06 pm
I published it, 5 years ago, when my dad was paralysed. It was 3 patents about network, DMA, and harddrive
Sorry to hear about your dad, @Olli. I hope all was well.
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 7:28 pm
by infratec
You still can use ConnectionId as variable. It comes not to a conflict.
Normally the PB procedures returns a PB internal value. But sometimes you need to use API functions to reach your target.
And the API functions needs the system value and not the PB internal value.
For these cases therte are some PB procedures which returns you the system internal value.
In the case of a network connection it will return the socket handle of the underlaying os function.
With this socket handle you can use setsockopt_() for example.
Re: Control a called routine through RunProgram()
Posted: Fri Oct 21, 2022 9:49 pm
by Oso
infratec wrote: Fri Oct 21, 2022 7:28 pm
You still can use ConnectionId as variable. It comes not to a conflict. Normally the PB procedures returns a PB internal value. But sometimes you need to use API functions to reach your target. And the API functions needs the system value and not the PB internal value.
Thanks infratec I've got it. I hadn't realised variable naming was quite as flexible as that. I see what you mean about the system value.
Just to wrap this up and for anyone who may be interested, the code below provides a speed test of multiple processes. It only requires a single port 6000 for the main process server, but the sub-processes are numbered from 5000. I've tested it with 200 sub-processes. It keeps the programme ids. and the connection ids. in two arrays, prog.i() and connectid.i().
To use it run Mainprog.pb. Tell it how many processes to open and then you need to navigate back to the main process window. As soon as you press a key in the main window (not the sub-process windows), it will replicate that key out to all the other processes and they will reply back to the main process with a repeat of it. To close the sub-processes, press q. I know it's a bit of a weird test, but it shows the performance. Bear in mind the code is intended to be stored in d:\infratec_server_async\.
Code: Select all
; ** d:\infratec_server_async\Subprog.pb - Sub prog routine
OpenConsole()
*Buffer = AllocateMemory(1000)
Port = Val(ProgramParameter(0)) ; * Port is first parameter passed to this routine
id.s = ProgramParameter(1) ; * Id. enables this routine to pass back its ID string to be identified from others
PrintN("Subprog starting - ID : " + id.s + " - I'm listening as a server on port : " + port)
If CreateNetworkServer(0, Port, #PB_Network_IPv4 | #PB_Network_UDP, "127.0.0.1")
conid = OpenNetworkConnection("127.0.0.1", 6000, #PB_Network_UDP, 1000) ; * Connect back to calling routine's server
Repeat
If NetworkServerEvent() = #PB_NetworkEvent_Data ; * Test if this server has received data
result.i = ReceiveNetworkData(EventClient(), *Buffer, 1000) ; * Receive buffer contents from calling prc
If result
strinp$ = PeekS(*Buffer, result, #PB_UTF8|#PB_ByteLength) ; * Obtain the buffer contents
SendNetworkString(conid, ID.S + " Returning : " + strinp$) ; * Send confirmation back to calling proc.
output.s + strinp$ + #CRLF$ ; * Store the buffer so we can save to log
PrintN(strinp$) ; * Display the string received
If Left(strinp$,1) = "q" Or Left(strinp$,1) = "Q" ; * If told by server to quit (q/Q)
quit = 1 ; * Close this routine completely
EndIf
EndIf
Else
Delay(10)
EndIf
Until Quit = 1
CloseNetworkServer(0) ; * Close this routine's server
EndIf
Code: Select all
; ** d:\infratec_server_async\Mainprog.pb - Main calling routine
OpenConsole()
Dim prog.i(500)
Dim connectid.i(500)
*Buffer = AllocateMemory(2048)
Print("How many sub-processes do you wish to start? : ")
pcount.i = Val(Input())
For pno.i = 1 To pcount.i ; * Count through requested number of sub-processes and run each
prog(pno.i) = RunProgram("d:\infratec_server_async\subprog.exe", Str(pno.i + 4999) + " SUBPROG" + Str(pno.i), "", #PB_Program_Open)
connectid.i(pno.i) = OpenNetworkConnection("127.0.0.1", pno.i + 4999 , #PB_Network_UDP) ; * Open to sub-process server
PrintN("Connection id. : " + Str(connectid.i(pno.i))) ; * Connection id. to sub-proc
Next pno.i
server.i = CreateNetworkServer(#PB_Any, 6000, #PB_Network_UDP, "127.0.0.1") ; * Open this server side x 1
PrintN("Please proceed to type text to send to the background processes in real time (press q to quit : ")
Repeat
Key$ = Inkey() ; * Input key at a time
For pno.i = 1 To pcount.i ; * Go through each sub-proc.
SendNetworkString(connectid(pno.i), key$) ; * Send to each sub-process
Next pno.i ; * ... server
If key$ = "q" Or key$ = "Q" ; * This routine handles the
Exit = #True ; * quit function, sent just
EndIf ; * same as any other key
If NetworkServerEvent(server.i) = #PB_NetworkEvent_Data ; * Check received this server
cl.i = EventClient() ; * Client id. (this side only)
Length = ReceiveNetworkData(cl.i, *Buffer, MemorySize(*Buffer)) ; * No. of bytes in buffer
If Length ; * If something received
Received$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength) ; * Obtain the buffer contents
PrintN(Str(cl.i) + " " + Received$)
EndIf
EndIf
Until Exit
FreeMemory(*Buffer)
CloseNetworkServer(server.i) ; * Close this server
For pno.i = 1 To pcount.i ; * Go through each sub-process
CloseNetworkConnection(connectid(pno.i)) ; * Close connection to sub-proc
CloseProgram(prog(pno.i)) ; * Close programme space (but
Next pno.i ; * not killing it).
CloseConsole()