Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

client to server, socket in python many to one relationship

Hi I was able to connect my client to my server, but it has only one to one relationship( 1 client to server). My question in what should I do so that I can connect many client to my server? does anyone has an idea about my situation? any help will be appreciated, thanks in advance... Below is my codes.

server.py

import socket

s = socket.socket()
host = socket.gethostname()
port = 12349
s.bind((host, port))

s.listen(5)
while True:
    c, addr = s.accept()
    pressed = 0
    while True:
        print 'Got connection from', addr
        data = c.recv(1024)
        if not data:
            break
        pressed = pressed + 1
        print data, 'pressed count', pressed

client.py

import socket 
from Tkinter import*

root = Tk()

root.title("ADKOO")
root.geometry("150x80")

s = socket.socket()         # Create a socket object
host = socket.gethostname() # Get local machine name
port = 12349
s.connect((host, port))

def counterPlus():
  print "sending to"
  s.send('sent by '+host)


app = Frame(root)
app.grid()

button1 = Button(app, text="+", width=15, command=counterPlus)

button1.grid()
root.mainloop()
like image 838
gadss Avatar asked Dec 08 '22 13:12

gadss


1 Answers

When your server's socket receives a connect attempt from a client, s.accept() returns the a socket object and ip,port information. What you need to do is save this new socket object in a list and poll this list.

while True:
    c, addr = s.accept()
    clients.append(c)
    pressed = 0

Or better yet, use a dictionary of the ip:port tuple, and iterate over clients.iteritems()

while True:
    c,addr = s.accept()
    clients[addr] = c

A naive implementation might be:

import socket
clients={}
server = socket.socket()
host = socket.gethostname()
port = 12349
s.bind((host, port))

s.listen(5)
while True:
    c, addr = server.accept()
    clients[addr] = c
    pressed = 0
    for eachsocket, eachaddrtuple in clients.iteritems():
        print 'receiving data from %s'%eachaddrtuple
        data = c.recv(1024)
        if data:
            print data
        pressed = pressed + 1
        print data, 'pressed count', pressed

However, the problem with this implementation is that each loop of the 'while True:' block will result in a blocking call to server.accept(), meaning it will spin-loop on that line until a new client connects. As well, the c.recv(1024) call is also blocking, meaning that if you have 15 clients, each client will need to send data before the loop iterates over again.

For example, if you have two clients, and one client doesnt send data for a minute, the other client is effectively cut off. The buffer can overflow and block the sending side, and other various nasty things can occur.

There is a way to set these arguments to act asynchronously, but it escapes me right now. The good thing about asynchronous calls is that it will let you handle multiple clients, with proper error checking for no data received. However, this does not scale well for medium-large numbers of clients on one server.

One way to overcome this, at least on linux machines, (and windows, with only sockets) is to use the python 'select' module. The select module, to paraphrase, will check the sockets for any waiting connections or data.

A more robust server that can handle multiple clients is the following:

import select 
import socket 
import sys 

host = 'localhost' # what address is the server listening on 
port = 50000 # what port the server accepts connections on
backlog = 5  # how many connections to accept
maxsize = 1024 # Max receive buffer size, in bytes, per recv() call

#now initialize the server and accept connections at localhost:50000

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind((host,port)) 
server.listen(backlog) 
input = [server,] #a list of all connections we want to check for data 
                  #each time we call select.select()

running = 1 #set running to zero to close the server
while running: 
  inputready,outputready,exceptready = select.select(input,[],[]) 

  for s in inputready: #check each socket that select() said has available data

    if s == server: #if select returns our server socket, there is a new 
                    #remote socket trying to connect
      client, address = server.accept() 
      input.append(client) #add it to the socket list so we can check it now
      print 'new client added%s'%str(address) 

    else: 
      # select has indicated that these sockets have data available to recv
      data = s.recv(maxsize) 
      if data:
        print '%s received from %s'%(data,s.getsockname())

        #Uncomment below to echo the recv'd data back 
        #to the sender... loopback!
        #s.send(data) 
      else: #if recv() returned NULL, that usually means the sender wants
            #to close the socket. 
        s.close() 
        input.remove(s) 

#if running is ever set to zero, we will call this
server.close()

This allows us to have three lists. One for inputs, one for outputs, and one for exceptions. Ignore exceptions for now. On every loop, select.select will return three lists with any objects waiting for new data to be read or written.

So every time the server socket receives a connection attempt, it will be returned by 'select' in the 'inputready' list. Similarly, any clients that have sent data to the server will be returned in the same 'inputready' list, so you must check to see which socket was returned to handle it appropriately.

Note: The server socket you initially create is only used here to handle incoming connection attempts.

There are python frameworks such as Twisted that allow you to create simple servers by settings a few parameters, but I am not familiar with it. It may be worth looking into, as low level socket programming is very unforgiving.

As an aside, note that disconnects and sends from the server to clients are not handled in this framework presented above, although it can be implemented. Disconnects are signaled from one end by sending a packet with 0 length data. So the c.recv() call will return an empty string.

like image 184
gregb212 Avatar answered Apr 07 '23 11:04

gregb212