select.select issue for sockets and pipes

ghz 昨天 ⋅ 2 views

I am currently in the process of writing a basic python script that makes use of both pipes and sockets. The pipe currently holds incoming data from an html form, the socket makes up a connection to a server that sends TCP/IP commands at various intervals. The form and server are on the same LAN but different computers.

The code I have is as follows:

#!/usr/bin/env python
import os,sys,time
from socket import *

HOST = 'xxx.xxx.xxx.xxx'
PORT = 6000
BUFSIZ = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
count = 0
pipe_name = 'testpipe'

if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

def readpipe():
    pipein = open(pipe_name, 'r')
    line = pipein.readline()[:-1]
    print line
    pipein.close()

def readTCP():
    data = tcpCliSock.recv(BUFSIZ)
    print data

while count == 0:
    readTCP()
    readpipe()

I realise the count variable is currently redundant, I have been using it to try and debug some earlier issues

As you can see the code is pretty straightforward and works as long as data comes in first on the pipe then the socket and keeps this pattern. The problem is that it is not dynamically running through both functions. It hangs while waiting for input at either the pipe of socket. I am trying to find a way to identify if no data is present on either the pipe or socket, then it can go off and check the other. Everything I have tried so far has had no effect such as checking for an empty string.

I have found some interesting stuff on the following post but it seems quite a bit different to what I am doing (using stdin) so I'm not sure if it is what I'm really looking for or whether it could be adapted;

Visit What's the best way to tell if a Python program has anything to read from stdin?

Following on from the advice by 'User' I have now put together a bit of additional code:

while True:
   inputdata,outputdata,exceptions = select.select([tcpCliSock],[],[])
   if tcpCliSock in inputdata:
      data = tcpCliSock.recv(BUFSIZ)
      print data

although this works I have not been able to discover what identifier to put into inputdata for the pipe. I have read up on the select module and apparently it does work with pipes but there seems to be very little info on it, it's all networked stuff. I have tried the following (taken from main code above):

 testpipe
 pipe_name
 pipein

But i get various errors most of which are related to a NameError - not defined or TypeError: argument must be an int, or have a fileno() method

Answer

You're on the right track with select.select(); it allows you to monitor multiple file descriptors (including pipes and sockets) for availability. The error you're seeing is likely due to not providing the correct object that can be checked by select.select(). select.select() works with objects that have a fileno() method, such as file objects, sockets, or pipes.

In the case of your pipe, the issue is that open(pipe_name, 'r') returns a file object, which doesn't work directly with select without using its fileno() method. You need to work with the file descriptor of the pipe, which you can get by calling pipein.fileno().

Here's how you can adapt your code to use select.select() for both the pipe and the socket:

#!/usr/bin/env python
import os
import time
import select
from socket import *

HOST = 'xxx.xxx.xxx.xxx'
PORT = 6000
BUFSIZ = 1024
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
count = 0
pipe_name = 'testpipe'

if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

def readpipe():
    with open(pipe_name, 'r') as pipein:
        line = pipein.readline().strip()
        print("Pipe data:", line)

def readTCP():
    data = tcpCliSock.recv(BUFSIZ)
    print("Socket data:", data)

while True:
    # Prepare the file descriptors to monitor (socket and pipe)
    pipein = open(pipe_name, 'r')
    inputs = [tcpCliSock, pipein]
    inputdata, _, _ = select.select(inputs, [], [])

    for item in inputdata:
        if item is tcpCliSock:
            readTCP()
        elif item is pipein:
            readpipe()

Key Changes:

  1. Using select.select(): We include both the socket (tcpCliSock) and the pipe file object (pipein) in the inputs list.
  2. fileno() is automatically handled: select.select() works with file-like objects like sockets or files, so no need to explicitly call fileno().
  3. Using with open(pipe_name, 'r'): This ensures that the pipe is properly opened and closed within the loop.

Additional Notes:

  • select.select() will block until there is something to read from either the socket or the pipe.
  • The inputs list is checked for availability, and the corresponding function (readTCP() or readpipe()) is called when the respective data is ready.

This should allow your script to dynamically check both the pipe and the socket for new data without blocking on one or the other.