2013-09-10 3 views
3

Привет, я смог подключить своего клиента к моему серверу, но он имеет отношения только один к одному (1 клиент к серверу). Мой вопрос в том, что мне делать, чтобы я мог подключить много клиентов к моему серверу? кто-нибудь имеет представление о моей ситуации? любая помощь будет оценена, спасибо заранее ... Ниже приведены мои коды.клиент к серверу, сокет в python много к одному отношения

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() 

ответ

11

Когда сокет вашего сервера получает попытки соединения от клиента, s.accept() возвращает объект сокета и IP, порт Информация. Что вам нужно сделать, это сохранить этот новый объект сокета в списке и опросить этот список.

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

Или еще лучше, использовать словарь по IP: кортеж порта, и перебирать clients.iteritems()

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

Наивный реализация может быть:

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 

Однако , проблема с этой реализацией заключается в том, что каждый цикл блока while в True приведет к блокирующему вызову server.accept(), что означает, что он будет вращаться в этой строке до тех пор, пока не подключится новый клиент. Кроме того, вызов c.recv (1024) также блокирует, что означает, что если у вас 15 клиентов, каждый клиент должен будет отправить данные до повторения цикла снова.

Например, если у вас есть два клиента, и один клиент не отправляет данные в течение минуты, другой клиент фактически отключается. Буфер может переполнять и блокировать отправку, и могут возникнуть другие различные неприятные вещи.

Существует способ, чтобы эти аргументы выполнялись асинхронно, но это ускользает от меня прямо сейчас. Хорошая вещь об асинхронных вызовах заключается в том, что она позволит вам обрабатывать несколько клиентов с правильной проверкой ошибок для получения не полученных данных. Однако это не очень хорошо масштабируется для среднего числа клиентов на одном сервере.

Один из способов преодолеть это, по крайней мере, на машинах Linux (и windows, только сокетами) - использовать модуль «select» python. Модуль выбора, чтобы перефразировать, будет проверять сокеты для любых ожидающих соединений или данных.

Более надежный сервер, который может работать с несколькими клиентами заключается в следующем:

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() 

Это позволяет нам иметь три списка. Один для входов, один для выходов и один для исключений. Игнорировать исключения на данный момент. В каждом цикле select.select вернет три списка с любыми объектами, ожидающими чтения или записи новых данных.

Таким образом, каждый раз, когда сокет сервера получает попытку подключения, он возвращается «select» в списке «inputready». Аналогичным образом, все клиенты, которые отправили данные на сервер, будут возвращены в том же списке «inputready», поэтому вы должны проверить, какой сокет был возвращен для надлежащего обращения.

Примечание. Первоначально созданный вами серверный сокет используется только для обработки входящих попыток подключения.

Существуют фреймы python, такие как Twisted, которые позволяют создавать простые серверы по настройкам с несколькими параметрами, но я не знаком с ним. Возможно, стоит заглянуть в нее, поскольку программирование сокетов низкого уровня очень неумолимо.

Отметим, что в этой структуре, описанной выше, отключается и отправляется с сервера клиентам, хотя это может быть реализовано. Разъединения сигнализируются с одного конца, отправляя пакет с данными длины 0. Таким образом, вызов c.recv() возвращает пустую строку.

+0

Почти статья! – Matarata