There are a number of ways that you can design a connection-oriented socket server on the AS/400. While additional socket server designs are possible, the designs provided in the examples below are the most common:
In the iterative server example, a single server job handles all incoming connections and all data flows with the client jobs. When the accept() API completes, the server handles the entire transaction. This is the easiest server to develop, but it does have a few problems. While the server is handling the request from a given client, additional clients could be trying to get to the server. These requests fill the listen() backlog and some of the them will be rejected eventually.
All of the remaining examples are concurrent server designs. In these designs, the system uses multiple jobs and threads to handle the incoming connection requests. With a concurrent server there are usually multiple clients that connect to the server at the same time.
The spawn() server and spawn() worker example uses the spawn() API to create a new job to handle each incoming request. After spawn() completes, the server can then wait on the accept() API for the next incoming connection to be received. The only problem with this server design is the performance overhead of creating a new job each time a connection is received.
You can avoid the performance overhead of the spawn() server example by using prestarted jobs. Instead of creating a new job each time a connection is received, the incoming connection is given to a job that is already active. All of the remaining examples in this topic use prestarted jobs.
The givedescriptor() server and takedescriptor() worker example uses the AS/400-specific givedescriptor() and takedescriptor() APIs. These APIs pass the incoming connection to the worker (client) job. The server prestarted all of the worker jobs when the server job first started.
The sendmsg() server and recvmsg() worker example is nearly identical to the givedescriptor() server and takedescriptor() worker examples. The following are the main differences between the APIs:
In the previous examples, the worker job did not get involved until after the server received the incoming connection request. The multiple accept() servers and multiple accept() workers example of the system turns each of the worker jobs into an iterative server. The server job still calls the socket(), bind(), and listen() APIs. When the listen() call completes, the server creates each of the worker jobs and gives a listening socket to each one of them. All of the worker jobs then call the accept() API. When a client tries to connect to the server, only one accept() call completes, and that worker handles the connection. This type of design removes the need to give the incoming connection to a worker job, and saves the performance overhead that is associated with that operation. As a result, this design has the best performance.
A worker job or a worker thread refers to a process or subprocess (thread) that does data processing by using the socket descriptor. For example, a worker process accesses a database file to extract and format information for sending to the remote peer through the socket descriptor and its associated connection. It could then receive a response or set of data from the remote peer and update the database accordingly.
Depending on the design of the server, the worker usually does not perform the connection "bring-up" or initiation. This is usually done by the listening or server job or thread. The listening or server job usually passes the descriptor to the worker job or thread.
Notes:
| [ Information Center Home Page | Feedback ] | [ Legal | AS/400 Glossary ] |