0

I spawn few processes using the Python multiprocessing module. However when I call netstat -nptl, each ip:port listeners listed under the same PID.

I'm using Python 2.7 on Ubuntu 14.04.

netstat -V
>> net-tools 1.60
>> netstat 1.42 (2001-04-15)

Relevant code:

import unittest
import multiprocessing
import socket
import os
import time
import ex1


class Listener(multiprocessing.Process):
    def __init__(self, _ttl):
        super(Listener, self).__init__()
        self.ttl = _ttl
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind(('localhost', 0))

    def get_pid(self):
        return self.pid

    def get_name(self):
        return self.socket.getsockname()

    def run(self):
        self.socket.listen(1)
        time.sleep(self.ttl)

    def listen(self):
        self.start()


class TestEx1(unittest.TestCase):
    def test_is_legal_ip(self):
        # Legal IP
        assert(ex1.is_legal_ip("1.1.1.1:55555"))
        assert(ex1.is_legal_ip("0.1.1.255:55555"))
        assert(ex1.is_legal_ip("0.0.0.0:55555"))
        assert(ex1.is_legal_ip("255.255.255.255:55555"))
        assert(ex1.is_legal_ip("0.1.2.3:55555"))

        # Illegal IP
        assert(not ex1.is_legal_ip("256.1.1.1:55555"))
        assert(not ex1.is_legal_ip("1.256.1:55555"))
        assert(not ex1.is_legal_ip("1.1.1.1.1:55555"))
        assert(not ex1.is_legal_ip("1.a.1.1:55555"))
        assert(not ex1.is_legal_ip("1.1001.1.1:55555"))

    def test_address_to_pid(self):
        # Create 3 listener processes
        listener1 = Listener(22)
        listener2 = Listener(22)
        listener3 = Listener(22)

        # Start listening
        listener1.listen()
        listener2.listen()
        listener3.listen()

        print listener1.get_pid()
        print listener2.get_pid()
        print listener3.get_pid()


        # For each listener, get appropriate ip:port
        address1 = str(str(listener1.get_name()[0])) + \
            ":" + str(listener1.get_name()[1])
        address2 = str(str(listener2.get_name()[0])) + \
            ":" + str(listener2.get_name()[1])
        address3 = str(str(listener3.get_name()[0])) + \
            ":" + str(listener3.get_name()[1])

        # Check if address_to_pid() works as expected.
        #assert(str(ex1.address_to_pid(address1)) == str(listener1.get_pid()))
        #assert(str(ex1.address_to_pid(address2)) == str(listener2.get_pid()))
        #assert(str(ex1.address_to_pid(address3)) == str(listener3.get_pid()))

        # Waits for the listener processes to finish
        listener2.join()
        listener2.join()
        listener3.join()


if __name__ == "__main__":
    unittest.main()

Output:

4193
4194
4195
..
----------------------------------------------------------------------
Ran 2 tests in 22.019s

OK

Netstat -nptl output:

(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -               
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -               
tcp        0      0 127.0.0.1:37529         0.0.0.0:*               LISTEN      4192/python     
tcp        0      0 127.0.0.1:53402         0.0.0.0:*               LISTEN      4192/python     
tcp        0      0 127.0.0.1:49214         0.0.0.0:*               LISTEN      4192/python     
tcp        1      0 192.168.46.136:49475    209.20.75.76:80         CLOSE_WAIT  2968/plugin_host
tcp       70      0 192.168.46.136:60432    91.189.92.7:443         CLOSE_WAIT  3553/unity-scope-ho
tcp6       0      0 ::1:631                 :::*                    LISTEN      -   
suztomo
  • 5,114
  • 2
  • 20
  • 21
triple fault
  • 13,410
  • 8
  • 32
  • 45

1 Answers1

0

Using my Mac OS 10.9.5 (Python 2.7.3), I could reproduce the same behavior. After several try-and-error, it turned out that it's because the socket objects are shared among the processes. (lsof -p <pid> helps to identify listening sockets.)

When I made following change to Listener class, each process started to listen on its own port number by its own PID.

def get_name(self):
    # this method cannot refer to socket object any more
    # self.sockname should be initialized as "not-listening" at __init__
    return self.sockname

def run(self):
    # Instantiate socket at run
    self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.socket.bind(('localhost', 0))
    self.sockname = self.socket.getsockname()
    # Listener knows sockname
    print self.sockname
    self.socket.listen(1)
    time.sleep(self.ttl)

This behavior should be the same as Ubuntu's Python and netstat.

self.sockname remains "not-listening" at original process

To listen on port as independent process, sockets need to be created at run method of a Listener object (New process calls this method after creating copy of the object). However variables updated in this copied object in the new process are not reflected to the original objects in original process.

suztomo
  • 5,114
  • 2
  • 20
  • 21