| Forum List • Thread List • Refresh • New Topic • Search • Previous • Next 1 | 1. Multithreading Rexx in Reginald #5062 Posted by: Billr 2004-09-10 01:59:07 | I am new to Reginald but familiar with Rexx.
I want to write a server REXX script that uses RXSOCK to create a socket/port that allows up to 5 clients to simultaneously connect. Then, whenever a client connects, I want to launch another REXX script in a separate thread to handle interaction with that particular client. So, the main REXX script just accepts connections, and multiple copies of the child script will be launched -- one instance of the child script for each client. I can't find a way to start multiple threads (each running its own REXX script). I know that I can start multiple processes (ie, multiple instances of Reginald's Script Launcher), but can I pass args (such as client socket ID returned by SockAccept) to a process so that the process can talk to that client? Is there an easy way to do this? | 2. Re: Multithreading Rexx in Reginald #5064 | Reginald (and REXX itself) doesn't have any standard support for creating threads. So, you can't service each client asynchronously via a separate thread for each client. But you can allow several clients to connect simultaneously, and manage interaction with them by sort of "jumping" from one client to the next. The way you do this is to use non-blocking mode for all your sockets. Then, you call SockSelect() to let it tell you what to do, and when to do it. SockSelect will call a function in your script whenever it has received some data from some client, or a client is ready to to be sent more data from you, or another client is attempting to connect. And it will tell you which client needs service. (Note: If you intend to use a REXX Dialog interface in your server script, then you should use SockAsyncSelect instead of SockSelect).
If you download the RXSOCK add-on from my web page at the below site, you'll see that the package includes a couple example scripts. ServerSelect2.rex shows you how to use SockSelect to allow upto 63 clients to simultaneously connect and be serviced. AsyncSelect.rex is basically the same thing, but using a REXX Dialog interface and SockAsyncSelect instead.
Normally, when you use a separate thread to service each client, you use blocking mode (ie, the default mode) for your sockets. That just means that, a call to SockSend() for example, will not return until all your bytes have been sent. (Likewise, a call to SockRead will not return until all the bytes you request have been received). For example, if you need to send a 5K long string to some client, then when you pass that string to SockSend(), it won't return until all those bytes are sent. So, it's like this:
SockSend(client, mystring) But when you do everything in one thread, then you just can't wait inside of a call to SockSend or SockRead for one client, because then the other clients will need to wait for your SockSend or SockRead to return before another client can be serviced. So you instead use non-blocking sockets. With a non-blocking socket, a call to SockSend or SockRead does only as much as it can do immediately, and then returns. The net result is that you may need to make several calls to SockSend() to send various "blocks" until all 5K is sent. SockSend will tell you how many bytes it was able to send immediately. And then SockSelect will tell you when that client is ready to be sent more bytes.
This is more work because you do have to keep track of each client socket's state. For example, you'll need a variable to count how many bytes have been sent with each call to SockSend until you count that all 5K have been sent. And you'll have to maintain separate variables for each client. The aforementioned examples demonstrate this concept.
So your socket loop looks more like this (in psuedo code):
Wait for SockSelect to tell me whenever one of 5 things happen: 1) Another client wants to connect. 2) One of my connected clients wants to disconnect. 3) One of my connected clients has sent me data. 4) One of my connected clients is ready for me to send more data (if I have some). 5) There has been an error with a send or read.
When one of the above happens, SockSelect calls a function you specify in your script. It passes you the client socket (handle) so you know which client needs attention. And it tells you what operation the client wants, for example, perhaps the client is ready to be sent more data, in which case, you would check your variables for that client to see if there is any data that you're waiting to send to that client.
Get RXSOCK at:
http://www.borg.com/~jglatt/rexx/win32/rxusrw32.htm | 3. Re: Multithreading Rexx in Reginald #5066 Posted by: Billr 2004-09-10 22:26:04 | What I am doing is creating a simple host simulator. On the client side I am using a 3270 or VT emulator, and on the server side I am using Rexx to create and distribute a Telnet connection followed by "host" screens and the logic to navigate them. So I set up a socket, and wait for a client connection. Then when one comes in I call a subroutine that does a simulation of a Telnet connection, then start sending out host screens. When responses come back from the client, my subroutine checks to see what keys were entered and them jumps to the next appropriate screen or error message. I have this working for a single client. Now I need to get it working for up to 5 clients.
I was hoping to either multi-thread or use separate processes so that each client could run out of a separate instance of the subroutine (with its own copies of various REXX variables to implement the logic). Otherwise, if I do everything from one instance of the subroutine, I have to maintain separate variables for each client (all from within the one subroutine), and keep track of every send/receive and remember where I am for each client. I would go crazy. | 4. Re: Multithreading Rexx in Reginald #5070 Posted by: Billr 2004-09-13 22:06:03 | I also have a question on your Rexx2Exe tool. I have a Main Rexx script that makes calls to 5 other Rexx scripts. I put them all into one exe with Rexx2Exe, but when the exe ran it failed when the Main script made its first CALL to one of the other sub-scripts. The failure message said the called script could not be found. | 5. Re: Multithreading Rexx in Reginald #5074 | When you call the script, make sure that you specify the .rex extension on the name. For example, to call the script named MyScript.rex:myreturn = myscript.rex(myarg) Rexx2Exe differentiates between a macro (has the .rex extension) and an internal function in your script (minus the extension). Rexx2Exe treats all of the child scripts as macros.
Reginald's FUNCDEF function allows your script to define and call a function in any DLL, even if that DLL isn't made for REXX. Since most operating systems have their API in DLLs, this means that your REXX script can directly call any operating system function. So you can roll your own support.
For example, you can use ShellExecute() or CreateProcess() to launch another running copy of the interpreter (ie, Reginald's Script Launcher) instead of using the start command. The former is more portable across all versions of Windows, and also gives you a lot more control over the process. For example, you can pass the args you need to the launched REXX script.
There is an example on my scripts page for FUNCDEF'ing and calling ShellExecute. In fact, the example launches a child rexx script as a separate process. (But my theory for your specific needs would be to use CreateProcess and allow it to use the handles of the parent process, and then pass it the Berkeley socket number of interest. Normally, different processes can't access each others' handles unless you tell Windows to allow that when you launch a process. The only problem with this scheme is that, my version of RXSOCK doesn't give you that handle. It gives you a special RXSOCK number that is only good in the process that created the socket. So, I'd have to get you an update to RXSOCK if you want to go that route -- an update that includes a function to get at that real handle. And then your client script would have to pass that to RXSOCK's SockGetHandle() before it could be used with other RXSOCK functions).
If you install the Reginald-specific version of my REXX book, then you can read more about FUNCDEF under DLL Functions. I'll see if I can whip up an example for CreateProcess, and see about an RXSOCK update. | 6. Re: Multithreading Rexx in Reginald #5075 | Ok, I did some work here. Here is what your server script needs to do:
OPTIONS "WINFUNC NOSOURCE"
LIBRARY rxsock
process_info = "32u,32u,32u,32u"
startup_info = "32u,32u,32u,32u,32u,32u,32u,32u,32u,32u,32u,32u,16u,16u,32u,32u,32u,32u"
FUNCDEF('CreateProcess', '32u, str, str, 32u, 32u, 32u, 32u, 32u, str, struct STARTUP_INFO, struct PROCESS_INFO stor', 'kernel32')
SockInit(10, 1)
DO
server.!family = "AF_INET"
server.!addr = "INADDR_ANY"
server.!port = 20248
server = SockSocket(, , , 'server.!', 5)
SAY "Socket created and waiting for connections from clients..."
moreclients:
client = SockAccept(server, 'client.!')
SAY "Client connected from address" client.!addr
berkeley = sockgethandle(client, 'B')
startup_info. = 0
startup_info.1 = 68
result = createprocess(, '"E:\Program files\Reginald\RxLaunch.exe" HandleMe.rex' berkeley, , , 1, 8, , , startup_info, process_info)
IF result == 0 THEN SAY "Failed to run."
SockClose(client, 1)
SIGNAL moreclients
CATCH user 10
SockPSock_Errno()
FINALLY
SockClose(server)
SAY "Socket closed"
END
RETURN
And here's what HandleMe.rex needs to do:
LIBRARY rxsock
SockInit(10, 1)
DO
berkeley = ARG(1)
client = sockgethandle(berkeley)
CATCH user 10
SockPSock_Errno()
FINALLY
SockClose(client, 1)
END
RETURN | 7. Re: Multithreading Rexx in Reginald #5087 Posted by: Billr 2004-09-15 21:51:38 | Hi Jeff. Thanks for all your help. Yes the changes did work. I now have the capability to communicate successfully to multiple clients with my scripts. I am in the process of cleaning them up. I need to eliminate the need to give an explicit path in the create process command and we are adding a UI. Today I will also be trying to get them to run after being saved in an EXE. If you want a copy of what I am doing let me know and I'll be happy to send you one directly. Are there any circumstances where you would be willing to distribute your source for RXSOCK or Reginald other RX functions with us? We would of course be happy to send you any updates we might make. Billr@wrq.com | 8. Re: Multithreading Rexx in Reginald #5090 | So the above code I posted worked for you? If so, I'll go ahead and add a simple server example (to the RXSOCK package) using the above technique.
If you want to contribute some examples, that would be very good. There are not a lot of examples of REXX coding in general, and specifically, a dirth of RXSOCK examples.
The RXSOCK C source is already on my RXSOCK web page (although I haven't yet uploaded the last change I made to accomodate the above code).
The Reginald sources are really large -- too large to host on my FTP site. And they are very complicated (although I've done a lot of commenting of code in the event that someone else ever has to maintain it), so it is not something that you want to work with unless you're prepared to spend a few months studying the code. I also need to clean up the sources. Things have gotten a bit messy over the past year as I added a bunch of new features to the interpreter. (And there are lots of harmless warning messages that I haven't yet bothered to clean up). Furthermore, the sources will soon be undergoing a bunch more changes as I port Reginald to Linux. (Yes, I finally got around to installing SUSE Linux, so Reginald for Linux is coming -- as soon as I learn enough about Linux programming to port the various Windows-specific features to Linux). If you're really serious about working with the sources, I can see about getting them to you. But it would be such a hassle for me to try to maintain an up-to-date archive of them on the web, that I'd rather avoid having to do that work if no one really is going to be doing much with those sources.
My email address is in the REXX book, on the Errata page. | Forum List • Thread List • Refresh • New Topic • Search • Previous • Next 1 |
|
|