Orjanp
Orjanp

Reputation: 10921

How to make server accepting connections from multiple ports?

How can I make a simple server(simple as in accepting a connection and print to terminal whatever is received) accept connection from multiple ports or a port range?

Do I have to use multiple threads, one for each bind call. Or is there another solution?

The simple server can look something like this.

def server():
import sys, os, socket

port = 11116
host = ''
backlog = 5 # Number of clients on wait.
buf_size = 1024

try:
    listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listening_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
    listening_socket.bind((host, port)) 
    listening_socket.listen(backlog)
except socket.error, (value, message):
    if listening_socket:
        listening_socket.close()
    print 'Could not open socket: ' + message
    sys.exit(1)

while True:
    accepted_socket, adress = listening_socket.accept()

    data = accepted_socket.recv(buf_size)
    if data:
        accepted_socket.send('Hello, and goodbye.')
    accepted_socket.close()

server()

EDIT: This is an example of how it can be done. Thanks everyone.

import socket, select

def server():
import sys, os, socket

port_wan = 11111
port_mob = 11112
port_sat = 11113

sock_lst = []
host = ''
backlog = 5 # Number of clients on wait.
buf_size = 1024

try:
    for item in port_wan, port_mob, port_sat:
        sock_lst.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
        sock_lst[-1].setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
        sock_lst[-1].bind((host, item)) 
        sock_lst[-1].listen(backlog)
except socket.error, (value, message):
    if sock_lst[-1]:
        sock_lst[-1].close()
        sock_lst = sock_lst[:-1]
    print 'Could not open socket: ' + message
    sys.exit(1)

while True:
    read, write, error = select.select(sock_lst,[],[])

    for r in read:
        for item in sock_lst:
            if r == item:
                accepted_socket, adress = item.accept()

                print 'We have a connection with ', adress
                data = accepted_socket.recv(buf_size)
                if data:
                    print data
                    accepted_socket.send('Hello, and goodbye.')
                accepted_socket.close()

server()

Upvotes: 5

Views: 18913

Answers (3)

Anon
Anon

Reputation: 31

Since Python's got so much overhead, multithreaded apps are a big point of debate. Then there's the whole blocking-operation-GIL issue too. Luckily, the Python motto of "If it seems like a big issue, someone's probably already come up with a solution (or several!)" holds true here. My favorite solution tends to be the microthread model, specifically gevent.

Gevent is an event-driven single-thread concurrency library that handles most issues for you out of the box via monkey-patching. gevent.monkey.patch_socket() is a function that replaces the normal socket calls with non-blocking variants, polling and sleeping to allow the switch to other greenlets as need be. If you want more control, or it's not cutting it for you, you can easily manage the switching with select and gevent's cooperative yield.

Here's a simple example.

import gevent
import socket
import gevent.monkey; gevent.monkey.patch_socket()

ALL_PORTS=[i for i in xrange(1024, 2048)]
MY_ADDRESS = "127.0.0.1"    

def init_server_sock(port):
    try: 
        s=socket.socket()
        s.setblocking(0)
        s.bind((MY_ADDRESS, port))
        s.listen(5)
        return s
    except Exception, e:
        print "Exception creating socket at port %i: %s" % (port, str(e))
        return False

def interact(port, sock):
    while 1:
        try:
            csock, addr = sock.accept()
        except:
            continue
        data = ""
        while not data:
            try:
                data=csock.recv(1024)
                print data
            except:
                gevent.sleep(0) #this is the cooperative yield
        csock.send("Port %i got your message!" % port)
        csock.close()
        gevent.sleep(0)


def main():
   socks = {p:init_server_sock(p) for p in ALL_PORTS}
   greenlets = []
   for k,v in socks.items():
       if not v:
           socks.pop(k)
       else:
           greenlets.append(gevent.spawn(interact, k, v))

   #now we've got our sockets, let's start accepting
   gevent.joinall(greenlets)

That would be a super-simple, completely untested server serving plain text We got your message! on ports 1024-2048. Involving select is a little harder; you'd have to have a manager greenlet which calls select and then starts up the active ones; but that's not massively hard to implement.

Hope this helps! The nice part of the greenlet-based philosophy is that the select call is actually part of their hub module, as I recall, which will allow you to create a much more scalable and complex server more easily. It's pretty efficient too; there are a couple benchmarks floating around.

Upvotes: 3

Mark Rushakoff
Mark Rushakoff

Reputation: 258198

If you really wanted to be lazy (from a programmer standpoint, not an evaluation standpoint), you could set a timeout on your blocking read and just loop through all your sockets; if a timeout occurs, there wasn't any data available. Functionally, this is similar to what the select is doing, but it is taking that control away from the OS and putting it in your application.

Of course, this implies that as your sleep time gets smaller, your program will approach 100% CPU usage, so you wouldn't use it on a production app. It's fine for a toy though.

It would go something like this: (not tested)

def server():
    import sys, os, socket

    port = 11116
    host = ''
    backlog = 5 # Number of clients on wait.
    buf_size = 1024
    NUM_SOCKETS = 10
    START_PORT = 2000

    try:
            socket.setdefaulttimeout(0.5) # raise a socket.timeout error after a half second
            listening_sockets = []
            for i in range(NUM_SOCKETS):
                listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                listening_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
                listening_socket.bind((host, START_PORT + i)) 
                listening_socket.listen(backlog)
                listening_sockets.append(listening_socket)
    except socket.error, (value, message):
            if listening_socket:
                    listening_socket.close()
            print 'Could not open socket: ' + message
            sys.exit(1)

    while True:
            for sock in listening_sockets:
                try:
                    accepted_socket, adress = sock_socket.accept()

                    data = sock.recv(buf_size)
                    if data:
                            sock_socket.send('Hello, and goodbye.')
                    sock.close()
                except socket.timeout:
                    pass

    server()

Upvotes: 2

Eric Petroelje
Eric Petroelje

Reputation: 60498

I'm not a python guy, but the function you are interested in is "select". This will allow you to watch multiple sockets and breaks out when activity occurs on any one of them.

Here's a python example that uses select.

Upvotes: 7

Related Questions