Chapter 7. Handling many sockets by spawning jobs

Table of Contents
7.1. Overview of job spawning approach
7.2. The takedescriptor() and givedescriptor() API calls
7.3. Retrieving the internal job id
7.4. Communicating the job information
7.5. Our server as a multi-job server
7.6. Working towards our next example
7.7. Validating a user-id and password
7.8. Running with the user's authority
7.9. A "generic server" example
7.10. Trying the "generic server" out
Written by Scott Klement.

7.1. Overview of job spawning approach

The last chapter explained in detail how to write server programs that could handle many simultaneous clients, all being served by one program in one job.

As I explained in the introduction to that chapter, that type of coding has it's advantages. It's main advantages are that the various connected clients can share data very easily through the server, and that it uses less system resources than some of the other methods of handling multiple clients.

It has some big disadvantages too! The coding can get very complicated! If something that one client is doing should crash the program, it'll crash all of the other clients as well! Everything in that server must run with the same user profile, so all client's have the same authority to system settings.

This chapter will discuss the opposite method. This method will use the SBMJOB command to "spawn" a new job for every client that connects. It will then become the new job's responsibility to send & receive data from that client.

Since the operating system will handle the multitasking for us, and the operating system is undoubtedly better at it than our programs are, this will give better performance for very network-intensive applications.

It will however, make communications between the various connected clients extremely difficult. You'd have a very hard time writing a 'chat room' using this method -- even if you did get it to work, it would almost certainly be less efficient.

The 'job spawning approach' requires two (or more) programs in order to do it's basic duty. The first program is the 'listener' program. It listens and accepts new client connections, then submits a job to handle each client. The second program is the 'server instance' program, it handles all of the socket communications for a single client.

Here's the basic pseudocode of the 'listener program':

  1. A socket is created that listens for connections.

  2. accept() is called to receive a new connection.

  3. a new job is submitted to handle this new connection.

  4. We call the givedescriptor() API to tell the operating system that its okay for the new job to take the client's socket descriptor from us.

  5. We close() the client's descriptor, we're done with it.

  6. Go back to step #2.

Here's the basic pseudocode of the 'server instance' program:

  1. Communicate our job information back to the listener program so that it knows who to give a descriptor to.

  2. Call the takedescriptor() API to take the client's socket descriptor from the listener program.

  3. send() and recv() data to/from the client application.

  4. close the socket

  5. end.