48

So I am working on an iPhone app that requires a socket to handle multiple clients for online gaming. I have tried Twisted, and with much effort, I have failed to get a bunch of info to be sent at once, which is why I am now going to attempt socket.

My question is, using the code below, how would you be able to have multiple clients connected? I've tried lists, but I just can't figure out the format for that. How can this be accomplished where multiple clients are connected at once and I am able to send a message to a specific client?

Thank you!

#!/usr/bin/python           # This is server.py file

import socket               # Import socket module
s = socket.socket()         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 50000                # Reserve a port for your service.

print 'Server started!'
print 'Waiting for clients...'

s.bind((host, port))        # Bind to the port
s.listen(5)                 # Now wait for client connection.
c, addr = s.accept()     # Establish connection with client.
print 'Got connection from', addr
while True:
   msg = c.recv(1024)
   print addr, ' >> ', msg
   msg = raw_input('SERVER >> ')
   c.send(msg);
   #c.close()                # Close the connection
Alec
  • 919
  • 2
  • 10
  • 25
  • 4
    Make a while of `c, addr = s.accept()` when a client connected store them in a client_list, and then start a Thread of it. – Mayli May 30 '12 at 05:12
  • 2
    Would it be like.... while: c, addr = s.accept() client_list = [] client_list.append(c, addr) – Alec May 30 '12 at 05:16
  • 1
    I know it's a _huge_ change, but consider [Ruby's EventMachine](https://github.com/eventmachine/eventmachine/wiki/General-Introduction) -- I found the EventMachine documentation significantly easier to read and understand than the Twisted documentation. The downside to Ruby is the smaller ecosystem of library bindings and packages, but it's better than it once was... – sarnold May 30 '12 at 05:18
  • @sarnold I would rather stick with Python, I just feel it would be good to stick with it. Thanks for your suggestion though. – Alec May 30 '12 at 05:19
  • @AlecK. You need to have the `client_list=[]` out of the while loop, a good example is [here](http://stackoverflow.com/questions/3670127/python-socketserver-sending-to-multiple-clients), if you want just C-c C-v. – Mayli May 30 '12 at 05:20
  • 2
    @AlecK., yeah, I can understand, especially if you've got other code written in Python. It just strikes me every time I see Twisted documentation how complicated they've made some things... – sarnold May 30 '12 at 05:21
  • @Mayli I am not very experienced in this, so I can't quite figure it out. I'm not understanding it good enough. – Alec May 30 '12 at 05:23
  • @AlecK. Did you check the example link? Modify that example then run it, and you will get what you want. The Server class could handle multiple clients. If you want to learn more, read the code. – Mayli May 30 '12 at 05:29
  • @Mayli So I can't just use lists? It has to be that in-depth? I know when I used Twisted it seemed simpler. – Alec May 30 '12 at 05:32
  • @AlecK. Twisted is a more complex twisted asynchronous lib than the example give. If you are making a really complex project, it's worth spending time learning it. When you using lists, it more like a synchronous way in which your code is blocked in every network communication. – Mayli May 30 '12 at 05:40
  • @Mayli Can you give me a start in this while loop? I just can't think right now. :( – Alec May 30 '12 at 05:42
  • A thread per client does not scale. I know Twisted is hard to get into, but once you have some experience with it, it is the goto-tool for almost all network related stuff. Stuff like the one you're trying to solve. – Daniel F Oct 12 '18 at 21:19

8 Answers8

56

Based on your question:

My question is, using the code below, how would you be able to have multiple clients connected? I've tried lists, but I just can't figure out the format for that. How can this be accomplished where multiple clients are connected at once and I am able to send a message to a specific client?

Using the code you gave, you can do this:

#!/usr/bin/python           # This is server.py file                                                                                                                                                                           

import socket               # Import socket module
import thread

def on_new_client(clientsocket,addr):
    while True:
        msg = clientsocket.recv(1024)
        #do some checks and if msg == someWeirdSignal: break:
        print addr, ' >> ', msg
        msg = raw_input('SERVER >> ')
        #Maybe some code to compute the last digit of PI, play game or anything else can go here and when you are done.
        clientsocket.send(msg)
    clientsocket.close()

s = socket.socket()         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 50000                # Reserve a port for your service.

print 'Server started!'
print 'Waiting for clients...'

s.bind((host, port))        # Bind to the port
s.listen(5)                 # Now wait for client connection.

print 'Got connection from', addr
while True:
   c, addr = s.accept()     # Establish connection with client.
   thread.start_new_thread(on_new_client,(c,addr))
   #Note it's (addr,) not (addr) because second parameter is a tuple
   #Edit: (c,addr)
   #that's how you pass arguments to functions when creating new threads using thread module.
s.close()

As Eli Bendersky mentioned, you can use processes instead of threads, you can also check python threading module or other async sockets framework. Note: checks are left for you to implement how you want and this is just a basic framework.

rkta
  • 3,959
  • 7
  • 25
  • 37
jcchuks
  • 881
  • 8
  • 16
  • 1
    Could you check this again. I got this error " data = clientsocket.recv(16384) AttributeError: 'tuple' object has no attribute 'recv'" – hoangpx Jan 10 '17 at 02:59
  • 2
    The `clientsocket.close()` after the `while True:` in the function and the `s.close()` after the `while True:` at the bottom will never be called, since there are no `break`s in the `while True:` and the only way to exit the loop is with an exception, that will not be caught and will end the thread. A context manager would be a better way to make sure the socket is closed. – nog642 Jul 22 '19 at 18:36
  • I have adapted your program for python3 and it is coming up with this error Traceback (most recent call last): File "mydir\Server.py", line 29, in print(f"Got connection from \"{addr}\"") NameError: name 'addr' is not defined – NaNdy Apr 01 '20 at 15:12
  • 5
    Shouldn't `import thread` read `from threading import Thread` and `thread.start_new_thread()` then be replaced by `Thread(target=on_new_client, args=(c,addr))` at least for Python 3? – Jonathan Oct 18 '20 at 14:44
23

accept can continuously provide new client connections. However, note that it, and other socket calls are usually blocking. Therefore you have a few options at this point:

  • Open new threads to handle clients, while the main thread goes back to accepting new clients
  • As above but with processes, instead of threads
  • Use asynchronous socket frameworks like Twisted, or a plethora of others
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • 6
    Towards the 3rd point, Python now (3.5 onwards) has an in-built support of "asyncio" for asynchronous programming – ViFI Jun 02 '18 at 14:20
8

Here is the example from the SocketServer documentation which would make an excellent starting point

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print "{} wrote:".format(self.client_address[0])
        print self.data
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()

Try it from a terminal like this

$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello
HELLOConnection closed by foreign host.
$ telnet localhost 9999
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Sausage
SAUSAGEConnection closed by foreign host.

You'll probably need to use A Forking or Threading Mixin too

Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
2

This program will open 26 sockets where you would be able to connect a lot of TCP clients to it.

#!usr/bin/python
from thread import *
import socket
import sys

def clientthread(conn):
    buffer=""
    while True:
        data = conn.recv(8192)
        buffer+=data
        print buffer
    #conn.sendall(reply)
    conn.close()

def main():
    try:
        host = '192.168.1.3'
        port = 6666
        tot_socket = 26
        list_sock = []
        for i in range(tot_socket):
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
            s.bind((host, port+i))
            s.listen(10)
            list_sock.append(s)
            print "[*] Server listening on %s %d" %(host, (port+i))

        while 1:
            for j in range(len(list_sock)):
                conn, addr = list_sock[j].accept()
                print '[*] Connected with ' + addr[0] + ':' + str(addr[1])
                start_new_thread(clientthread ,(conn,))
        s.close()

    except KeyboardInterrupt as msg:
        sys.exit(0)


if __name__ == "__main__":
    main()
RaminNietzsche
  • 2,683
  • 1
  • 20
  • 34
1

This is what I've done in python 3.

import socket

from threading import Thread


def on_new_client(client_socket, addr):
    while True:
        data = client_socket.recv(1024).decode('utf-8')
        if not data:
            break
        print(f"{addr} >> {data}")
    client_socket.close()


def main():
    host = '0.0.0.0'  # allow any incoming connections
    port = 8888

    s = socket.socket()
    s.bind((host, port))  # bind the socket to the port and ip address

    s.listen(5)  # wait for new connections

    while True:
        c, addr = s.accept()  # Establish connection with client.
        # this returns a new socket object and the IP address of the client
        print(f"New connection from: {addr}")
        thread = Thread(target=on_new_client, args=(c, addr))  # create the thread
        thread.start()  # start the thread
    c.close()
    thread.join()


if __name__ == '__main__':
    main()
MattieG4
  • 150
  • 2
  • 12
0
def get_clients():
    first_run = True
    startMainMenu = False

    while True:
        if first_run:
            global done
            done = False
            Thread(target=animate, args=("Waiting For Connection",)).start()

        Client, address = objSocket.accept()
        global menuIsOn
        if menuIsOn:
            menuIsOn = False  # will stop main menu
            startMainMenu = True

        done = True

        # Get Current Directory in Client Machine
        current_client_directory = Client.recv(1024).decode("utf-8", errors="ignore")

        # beep on connection
        beep()
        print(f"{bcolors.OKBLUE}\n***** Incoming Connection *****{bcolors.OKGREEN}")
        print('* Connected to: ' + address[0] + ':' + str(address[1]))
        try:
            get_client_info(Client, first_run)
        except Exception as e:
            print("Error data received is not a json!")
            print(e)
        now = datetime.now()
        current_time = now.strftime("%D %H:%M:%S")
        print("* Current Time =", current_time)

        print("* Current Folder in Client: " + current_client_directory + bcolors.WARNING)

        connections.append(Client)

        addresses.append(address)

        if first_run:
            Thread(target=threaded_main_menu, daemon=True).start()

            first_run = False
        else:
            print(f"{bcolors.OKBLUE}* Hit Enter To Continue.{bcolors.WARNING}\n#>", end="")
        if startMainMenu == True:
            Thread(target=threaded_main_menu, daemon=True).start()
            startMainMenu = False
0

I tried a lot to deal with TCPServer and multiple connections.

The most stable and easy way is to use ThreadingTCPServer

Thanks to the comment of PatrizioBertoni!

socks = socketserver.ThreadingTCPServer(("0.0.0.0", port), SockHandler)
rundekugel
  • 1,118
  • 1
  • 10
  • 23
-2
#!/usr/bin/python
import sys
import os
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)         
port = 50000

try:
    s.bind((socket.gethostname() , port))
except socket.error as msg:
    print(str(msg))
s.listen(10)
conn, addr = s.accept()  
print 'Got connection from'+addr[0]+':'+str(addr[1]))
while 1:
        msg = s.recv(1024)
        print +addr[0]+, ' >> ', msg
        msg = raw_input('SERVER >>'),host
        s.send(msg)
s.close()
Aditya Vartak
  • 380
  • 1
  • 13
Skiller Dz
  • 897
  • 10
  • 17