client server socket interaction (inetd)

T

Tim

Hi, I have an inetd service on freebsd that calls a program
(daemon.py) with which I want the remote user to communicate. I can
call daemon.py from the command line on the host machine and it works
fine.

What I don't understand is how to make my remote client script
actually communicate. If I'm understanding correctly, the code below
just takes a message and sends it to inetd and writes the stdout from
the process to the client.

How can I modify the code to send a response back? Here's the
outline of what I want to do:
(1) client sends the message to the server (client -> inetd ->
daemon.py),
(2) client receives output back from the server,
(3) client user responds to a question from the remote process
(4) client continues to receive output back.

where 2-3-4 happen as needed by the remote process. Cries out for a
while loop doesn't it? I just don't know what to put in it. Currently
I just have steps 1 and 2 working. Client sends one message and gets
all output back. If server asks a question, processes deadlock with
server waiting and client unable to respond.
thanks,
--Tim Arnold

# imports, constants set
#def line_buffer(sock):
# code to yield the string response in
# blocks of 1024 bytes

def client(ip,port,message):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((ip,port))
sock.send(message)
for line in line_buffer(sock):
print line
sock.close()

if __name__ == '__main__':
message = ' '.join(sys.argv[1:]) )
print 'working... %s %s' % (SERVER_IP,SERVER_PORT)
client(SERVER_IP,SERVER_PORT,message)
print 'done.'
 
M

Martin Gregorie

Hi, I have an inetd service on freebsd that calls a program (daemon.py)
with which I want the remote user to communicate. I can call daemon.py
from the command line on the host machine and it works fine.

What I don't understand is how to make my remote client script actually
communicate. If I'm understanding correctly, the code below just takes a
message and sends it to inetd and writes the stdout from the process to
the client.

How can I modify the code to send a response back?
The code you've shown would appear to be doing what you've specified,
though only you can know whether this is what you intended.
Each time you run the client it:
- connects to the server
- sends a request
- reads the response(s)
- closes the socket and exits.

If you run it a second time it should do the same again. Is this the case?

An inetd server should be started when a connection request is received.
It should read requests, sending a response to each request in turn,
until the connection is closed, when it will be stopped by inetd.

Without seeing the code for the server and the corresponding inetd
configuration line its not possible to say more.

BTW, I prefer xinetd to inetd - its configuration is much more modular
and easier to understand. If freebsd supports xinetd it may make life
easier if you use it rather than inetd.



Here's the outline
of what I want to do:
(1) client sends the message to the server (client -> inetd ->
daemon.py),
(2) client receives output back from the server, (3) client user
responds to a question from the remote process (4) client continues to
receive output back.

where 2-3-4 happen as needed by the remote process. Cries out for a
while loop doesn't it? I just don't know what to put in it. Currently I
just have steps 1 and 2 working. Client sends one message and gets all
output back. If server asks a question, processes deadlock with server
waiting and client unable to respond. thanks,
--Tim Arnold

# imports, constants set
#def line_buffer(sock):
# code to yield the string response in # blocks of 1024 bytes

def client(ip,port,message):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((ip,port))
sock.send(message)
for line in line_buffer(sock):
print line
sock.close()

if __name__ == '__main__':
message = ' '.join(sys.argv[1:]) )
print 'working... %s %s' % (SERVER_IP,SERVER_PORT)
client(SERVER_IP,SERVER_PORT,message) print 'done.'
 
B

bobicanprogram

Hi, I have an inetd service on freebsd that calls a program (daemon.py)
with which I want the remote user to communicate. I can call daemon.py
from the command line on the host machine and it works fine.
What I don't understand is how to make my remote client script actually
communicate. If I'm understanding correctly, the code below just takes a
message and sends it to inetd and writes the stdout from the process to
the client.
How can I modify the code to send a response back?

The code you've shown would appear to be doing what you've specified,
though only you can know whether this is what you intended.
Each time you run the client it:
- connects to the server
- sends a request
- reads the response(s)
- closes the socket and exits.

If you run it a second time it should do the same again. Is this the case?

An inetd server should be started when a connection request is received.
It should read requests, sending a response to each request in turn,
until the connection is closed, when it will be stopped by inetd.

Without seeing the code for the server and the corresponding inetd
configuration line its not possible to say more.

BTW, I prefer xinetd to inetd - its configuration is much more modular
and easier to understand. If freebsd supports xinetd it may make life
easier if you use it rather than inetd.

Here's the outline


of what I want to do:
(1) client sends the message to the server (client -> inetd ->
daemon.py),
(2) client receives output back from the server, (3) client user
responds to a question from the remote process (4) client continues to
receive output back.
where 2-3-4 happen as needed by the remote process. Cries out for a
while loop doesn't it? I just don't know what to put in it. Currently I
just have steps 1 and 2 working. Client sends one message and gets all
output back. If server asks a question, processes deadlock with server
waiting and client unable to respond. thanks,
--Tim Arnold
# imports, constants set
#def line_buffer(sock):
# code to yield the string response in # blocks of 1024 bytes
def client(ip,port,message):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((ip,port))
sock.send(message)
for line in line_buffer(sock):
print line
sock.close()
if __name__ == '__main__':
message = ' '.join(sys.argv[1:]) )
print 'working... %s %s' % (SERVER_IP,SERVER_PORT)
client(SERVER_IP,SERVER_PORT,message) print 'done.'


Have a look at the SIMPL toolkit (http://www.icanprogram.com/06py/
lesson1/lesson1.html). It will allow you to do exactly what you want
without having to dive into socket programming.

bob
 
T

Tim

Thanks Martin, you're right:
Each time you run the client it:
- connects to the server
- sends a request
- reads the response(s)
- closes the socket and exits.

that is exactly what it's doing. But. The server may encounter a
problem during the process and ask the user for more information like
'abort/retry' or something like that.

What my code does *not* do is allow the user to respond to such a mid-
process question (so the server can take in that information and
proceed with its process). The server can ask, but there's no
mechanism for the user to respond to a question.
Without seeing the code for the server and the corresponding inetd
configuration line its not possible to say more.

I'm not trying to be opaque, but the reason I left out the code for
the server (daemon.py) is that it works as expected when exec'd from
the command line. That is, the process begins, asks a question, gets
an answer and continues.

The inetd configuration is:
myservice stream tcp nowait root /local/daemon.py daemon.py
 
M

Martin Gregorie

But. The server may encounter a problem
during the process and ask the user for more information like
'abort/retry' or something like that.
Servers never ask the client for information: they are strictly request/
response handlers. To do what you're asking about, you'll need to change
both the client and server:

- the client must analyse every response and, if necessary, ask for
more input from the user. IMO it would be best to design it so it
can accept several commands, with 'quit' being one of them. Either
that or all request/response pairs must be designed so that the client
can always send a single request, output the result and exit no matter
what the server returns.

- the server must always return a response to every possible request
or log a failure reason, preferably to the system log which is
/var/log/messages in Linux, and die. The server must thoroughly
validate requests and return an adequate error message if errors were
found. I'd strongly suggest that every request generates a single
(newline terminated?) response message because that makes error
detection much easier. If the response is multi-line, send it as a
single line, possibly as a comma-separated list, and let the client
format it for display

I tend to precede every request and response message with a fixed length
byte count: this way the recipient process always knows how many bytes to
read and can report errors if the received length is wrong. Using an 'end
of message marker' (NEWLINE unless a message can legally contain internal
newlines) can also be useful. I tend to put an error flag in the response
message because this simplifies the client. I also design all messages to
be plain ASCII because this makes debugging a lot easier. If things
really turn to worms you can use wireshark to watch the traffic and read
it straight off the screen without needing to decode binary values, etc.
So, an invalid request and response might look like this:

Request: "0013,WHOIS,elvis\n"
Response: "0030,ERROR,Unknown request: WHOIS\n"
The inetd configuration is:
myservice stream tcp nowait root /local/daemon.py daemon.py
From what I recall about inetd that should be OK - I guess it must be
since you can get one request through, and I assume (you didn't say) that
a second request is also accepted without restarting the server.
 
T

Tim

Servers never ask the client for information: they are strictly request/
response handlers. To do what you're asking about, you'll need to change
both the client and server:

- the client must analyse every response and, if necessary, ask for
  more input from the user. IMO it would be best to design it so it
  can accept several commands, with 'quit' being one of them. Either
  that or all request/response pairs must be designed so that the client
  can always send a single request, output the result and exit no matter
  what the server returns.

- the server must always return a response to every possible request
  or log a failure reason, preferably to the system log which is
  /var/log/messages in Linux, and die. The server must thoroughly
  validate requests and return an adequate error message if errors were
  found. I'd strongly suggest that every request generates a single
  (newline terminated?) response message because that makes error
  detection much easier. If the response is multi-line, send it as a
  single line, possibly as a comma-separated list, and let the client
  format it for display

I tend to precede every request and response message with a fixed length
byte count: this way the recipient process always knows how many bytes to
read and can report errors if the received length is wrong. Using an 'end
of message marker' (NEWLINE unless a message can legally contain internal
newlines) can also be useful. I tend to put an error flag in the response
message because this simplifies the client. I also design all messages to
be plain ASCII because this makes debugging a lot easier. If things
really turn to worms you can use wireshark to watch the traffic and read
it straight off the screen without needing to decode binary values, etc.
So, an invalid request and response might look like this:

Request:   "0013,WHOIS,elvis\n"
Response:  "0030,ERROR,Unknown request: WHOIS\n"


From what I recall about inetd that should be OK - I guess it must be
since you can get one request through, and I assume (you didn't say) that
a second request is also accepted without restarting the server.

Thanks for helping me to understand. I don't know if you're familiar
with LaTeX, but that's part of what daemon.py calls via subprocess,
and that is the underlying process I wanted the user to be able to
interact with.

When LaTeX encounters a problem it stops processing, asks the user
what to do (like abort/retry, kind-of), and does whatever the user
says. The daemon.py script handles that okay from the command line,
but if I'm understanding you this will be much harder if not
impossible to accomplish from a client communicating over a socket.

I thought that maybe I needed to fork the socket (somehow) so it could
listen after the message is sent, but it looks like it will be more
complex.

thanks, I'm going to back to reading more on socket programming.
--Tim
 
M

Martin Gregorie

Thanks for helping me to understand. I don't know if you're familiar
with LaTeX, but that's part of what daemon.py calls via subprocess, and
that is the underlying process I wanted the user to be able to interact
with.
I've used nroff but never LaTeX - however, my understanding is that its
essentially a batch process.
When LaTeX encounters a problem it stops processing, asks the user what
to do (like abort/retry, kind-of), and does whatever the user says. The
daemon.py script handles that okay from the command line, but if I'm
understanding you this will be much harder if not impossible to
accomplish from a client communicating over a socket.
Are you sure it wouldn't be better to end the run, returning suitable
error messages to the user, who would correct the input file(s) and rerun
LaTex?
I thought that maybe I needed to fork the socket (somehow) so it could
listen after the message is sent, but it looks like it will be more
complex.
The easier way my be to give the user a login to a captive shell that's
restricted to running LateX, an editor and little else apart from a
results viewer. Bash and most decent editors can be restricted so the
user can't start a second, less restricted shell, and the range of
programs it can execute can be restricted by adjusting with $PATH.

You may also be able to find a menu program that can impose many of those
restrictions by replacing the login's shell by the menu program. I do
this as a matter of course: my menu program may get released one of these
days but it's not currently on a fit state to publish.

If you want to continue down the server path you should investigate popen.
It is part of the os module. You could design the server so a suitable
request runs LaTeX via popen, captures the stdout and stderr output, if
any, and returns that and the LaTeX termination status code to the client
as a response message.
 
N

Nobody

When LaTeX encounters a problem it stops processing, asks the user
what to do (like abort/retry, kind-of), and does whatever the user
says. The daemon.py script handles that okay from the command line,
but if I'm understanding you this will be much harder if not
impossible to accomplish from a client communicating over a socket.

The difficulty of achieving this is unrelated to the use of a socket. It's
an issue of whether you have a user available to respond to any prompts,
or whether the client has to be able to do this itself.

If you want to "script" an interactive program, you need to be able to
recognise prompts and respond appropriately. If the program was designed
to interact with a human, this can be far from straightforward. But the
method of communication (pipes versus sockets) isn't relevant.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top