Guidance
指路人
g.yi.org
Guidance Forums / Reginald Rexx / Multithreading Rexx in Reginald

Register 
注册
Search 搜索
首页 
Home Home
Software
Upload

  
Forum List • Thread List • Reply • Refresh • New Topic • Search • Previous • Next First 1 Last
Message1. 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?
Message2. Re: Multithreading Rexx in Reginald
#5064
Posted by: Jeff Glatt 2004-09-10 18:13:48
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:
/* Send a 5K string to the client. Assume mystring contains 5K of chars */
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
Message3. 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.
Message4. 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.
Message5. Re: Multithreading Rexx in Reginald
#5074
Posted by: Jeff Glatt 2004-09-14 15:37:41
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.
Message6. Re: Multithreading Rexx in Reginald
#5075
Posted by: Jeff Glatt 2004-09-14 17:40:04
Ok, I did some work here. Here is what your server script needs to do:

/* ServerSelect.rex
 *
 * Demonstrates an example of a server using RxSock 1.8
 * with Reginald REXX interpreter.
 *
 * This script runs on a server and waits for a client
 * to connect. When a client connects to the server,
 * then the server launches a child script named
 * HandleMe.rex as a separate, asynchronous process to
 * manage that client.
 */

/* Make things easy for us with WINFUNC option */
OPTIONS "WINFUNC NOSOURCE"

/* Register all RXSOCK functions */
LIBRARY rxsock

/* Register CreateProcess() */
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')

/* Tell RXSOCK that we want it to raise USER 10 condition for any
 * error (besides SYNTAX errors of course). We also want SockErrNo
 * set (instead of ErrNo)
 */
SockInit(10, 1)

DO

/* Fill in a sockaddr_in struct which we pass to SockSocket */
server.!family = "AF_INET" /* Must be AF_INET since RxSock supports only that */
server.!addr = "INADDR_ANY"  /* Let RxSock fill in the local computer's address */
server.!port = 20248   /* This is the port we use to communicate. The client must use the same port # */

/* Get a socket (of type SOCK_STREAM, and using TCP/IP networking protocol)
 * and bind it to the above port/address. Set it into "listening for
 * connections from clients" mode, and allow 5 clients to wait for
 * us to call SockAccept.
 */
server = SockSocket(, , , 'server.!', 5)
SAY "Socket created and waiting for connections from clients..."

moreclients:
/* SockAccept() will wait for and accept an incoming client connection
 * and return that client's socket
 */
client = SockAccept(server, 'client.!')

/* We got a client connecting */
SAY "Client connected from address" client.!addr

/* Get the Berkeley socket from what SockAccept() returned */
berkeley = sockgethandle(client, 'B')

/* Launch HandleMe.rex as a separate process. Pass it the
 * Berkeley socket
 */

/* Here we launch a child script named "HandleMe.rex". We launch
 * it by calling CreateProcess() and let it inherit any handles
 * we have open. We need to invoke the Reginald Script Launcher
 * which is RXLAUNCH.EXE. Note that we may need to supply the
 * full path in order for Windows to find it, and also we double
 * quote that path if it has spaces in it.
 *
 * After RXLAUNCH.EXE, we list the name of the rexx script to
 * run. Anything you add to this string after the script name
 * are comma-delimited arguments that will get passed to the
 * child script. For example, here we pass the arg "Berkeley"
 * to the child script.
 *
 * If CreateProcess() succeeds, it returns a 1. If it
 * fails, then it returns 0.
 */

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."

/* Close the client socket (but don't tell it we're closing its connection).
 * NOTE: I believe that both this script, and HandleMe.rex, will need
 * to do a SockClose on the handle. But I'm not absolutely sure of
 * this. If there is a problem, first try putting a Sleep() before
 * this call, and if worse comes to worse, comment it out.
 */
SockClose(client, 1)

/* At this point we loop around to SockAccept to wait for another
 * client to connect.
 */
SIGNAL moreclients

CATCH user 10
/* We told RXSOCK to raise the USER 10 condition if there is
 * ever any error (other than a SYNTAX error). So we CATCH
 * USER 10, and  REXX jumps here if it is raised.
 * So, if we're here, then some RXSOCK function has had an
 * error.
 *
 * CONDITION('D') reports the name of the RXSOCK function that
 * had the error. SIGL is the line number in our script where
 * we had the error. The variable named "SockErrNo" is set to an
 * RXSOCK "error name" such as "EINPROGRESS" or "ENETDOWN" etc.
 * We can pass this variable to the RXSOCK function SockErrMsg()
 * to get an appropriate error message or call SockPSock_Errno
 * to display a message box.
 */
 SockPSock_Errno()

FINALLY
  /* Close any open socket. NOTE: RxSock doesn't mind if you try
   * to close a socket that was never opened
   */
  SockClose(server)
  SAY "Socket closed"

END

RETURN



And here's what HandleMe.rex needs to do:

LIBRARY rxsock

SockInit(10, 1)

DO

/* Get the Berkeley socket passed to us and create an RXSOCK socket */
berkeley = ARG(1)
client = sockgethandle(berkeley)

/* Now you can SockSend and SockRecv with the client handle */

CATCH user 10
   SockPSock_Errno()

FINALLY
  /* When done, close the client socket (and tell it we're closing
   * its connection)
   */
  SockClose(client, 1)

END

RETURN
Message7. 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
Message8. Re: Multithreading Rexx in Reginald
#5090
Posted by: Jeff Glatt 2004-09-16 07:09:02
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 • Reply • Refresh • New Topic • Search • Previous • Next First 1 Last
掌柜推荐
 
 
 
 
 
 
 
 
 
 
 
 
© Fri 2024-3-29  Guidance Laboratory Inc.
Email:webmaster1g.yi.org Hits:0