SysOps
SysOps

Reputation: 51

Multi Port Network Application

I want to create a python network application that can run on multiple ports (ex: TCP:1234, TCP:5678, etc).

So I have lets say n number of Sockets, each listening for a client connection. I programmed a simple network application that listens to a range of ports but when I run the application it gets stuck at the listening phase of the first socket process!

How can I make my single python program when run to listen to N number of ports and each waiting for a client to connect to it. All sockets are running and listening at the same time.

Socket/Process #1: Listening on TCP Port 5000
Socket/Process #2: Listening on TCP Port 5001
Socket/Process #3: Listening on TCP Port 5002
...
Socket/Process #N: Listening on TCP Port 6000

Appreciate any ideas.

#!/usr/bin/env  python

import socket

def getPortList():
    ports=[]
    nPort=int(raw_input("# how many ports you want? "))
    j = 0
    for i in range(0,nPort):
        ports.append(int(raw_input("Enter port number: ")))
        j+=1
    return ports

def myTCPSocket(port=5000):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
    s.bind(("", int(port)))
    print ("\nWaiting for connections!\n")
    s.listen(5)
    (clientsock, clientaddr) = s.accept()
    print(clientaddr)

    data = "start"

    while len(data):
                clientsock.send("\nWelcome to Echo Server\n")
                data = clientsock.recv(1024)
                print ("Data sent is: ", data)
                clientsock.send(data)
                if data == "exit\r\n":
                        clientsock.close()

plst = getPortList()

for item in plst:
    myTCPSocket(item)

Upvotes: 3

Views: 1525

Answers (1)

abarnert
abarnert

Reputation: 365657

Listening on multiple sockets is really no different from listening on a single socket.

You already need to handle the listener socket and all client connection sockets somehow. You can do this by:

  • Writing a loop around select.select (or poll, kqueue, epoll, etc.).
  • Using the standard-library reactor asyncore.
  • Using a third-party reactor or proactor like Twisted.
  • Using OS-specific functionality (e.g., using a Cocoa runloop and server via PyObjC).
  • Creating a thread for each new connection.
  • Creating a subprocess for each new connection.

Almost all of these schemes will also work for dealing with multiple listeners. The simplest thing to do is to combine the two into one (e.g., a single select loop that handles all of their listeners and all of their client sockets, or a separate thread for each listener and client socket).

For performance or debugging reasons, you might want to instead use a two-tier hybrid approach (e.g., a thread for each listener, each with a select loop for all of its client sockets, or a process for each listener, each with a thread for each client socket). But if you don't have any good reason to do that, don't add the complexity.

http://pastebin.com/QebZMKz3 shows a simple single-select implementation. Here's the output:

$ ./multiserve.py 22222 22223 &
(('127.0.0.1', 22222), ' listening')
(('127.0.0.1', 22223), ' listening')
$ echo 'abc' | nc localhost 22222
(('127.0.0.1', 22222), ' <- ', ('127.0.0.1', 64633))
(('127.0.0.1', 64633), ' <- ', 'abc\n')
(('127.0.0.1', 64633), ' EOF')

If you think you'll never actually need to handle two simultaneous clients… well, you're probably wrong, but… You can use most of the above techniques, and it may be marginally simpler. For example, you can select on the listeners, and then do the accept and client-socket communication synchronously before returning to the loop. Or you can create a process or thread for each listener but handle the accept and client-socket communication synchronously within each. And so on.

http://pastebin.com/wLVLT49i shows a simple example that seems to be what you were trying to do. Since it uses a process for each socket (via os.fork), it does allow simultaneous connections on different ports; since it doesn't do anything asynchronously within each process, it doesn't allow simultaneous connections to the same port. (And of course it's POSIX-specific because it uses fork.)

If you're looking to learn how to write asynchronous network servers in the first place, I'd suggest you do two different implementations: select and threads. They conceptually fundamental, and relatively easy to code.

First, for select, you have to get your head around the idea of an event loop—the events are each new incoming connection, each incoming network packet on an existing connection, even each time a pipe you were writing to gets unclogged. The tricky bit here is that, as with any event loop, you need to handle each event and return without blocking, and without spending too much CPU time. For example, for an echo server, you can't just do a write on the other sockets, because some of them might be busy. So instead, you have to stick the output in a write buffer for each socket, and they'll get it in some future run through the event loop, when thye're ready.

Meanwhile, for threads, a separate thread for each connection seems like it makes everything trivial, but what happens when you need to echo a message from one thread to another? You either need some form of inter-thread communication, or shared data with inter-thread synchronization. So, you might have a Queue for writes on each socket, so any other socket's thread can just push a message onto the queue.

Neither of these will be as good as what a well-turned reactor or proactor can do, but it'd worth learning the basics—especially since you're going to face both the blocking issue (from select) and the communication issue (from threads) with any solution, and they'll be much more mysterious and harder to debug when you're working at a higher level.

Upvotes: 2

Related Questions