2009-04-22 5 views
16

J'écris actuellement un serveur telnet en Python. C'est un serveur de contenu. Les gens se connectent au serveur via telnet, et se voient présenter du contenu textuel.Plusieurs connexions réseau simultanées - Serveur Telnet, Python

Mon problème est que le serveur devrait évidemment supporter plus d'une connexion simultanée. La mise en œuvre actuelle que j'ai maintenant ne supporte qu'un seul.

Ceci est la base, la preuve de concept serveur j'ai commencé avec (alors que le programme a beaucoup changé au fil du temps, le cadre telnet de base n'a pas):

import socket, os 

class Server: 
    def __init__(self): 
     self.host, self.port = 'localhost', 50000 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.socket.bind((self.host, self.port)) 

    def send(self, msg): 
     if type(msg) == str: self.conn.send(msg + end) 
     elif type(msg) == list or tuple: self.conn.send('\n'.join(msg) + end) 

    def recv(self): 
     self.conn.recv(4096).strip() 

    def exit(self): 
     self.send('Disconnecting you...'); self.conn.close(); self.run() 
     # closing a connection, opening a new one 

    # main runtime 
    def run(self): 
     self.socket.listen(1) 
     self.conn, self.addr = self.socket.accept() 
     # there would be more activity here 
     # i.e.: sending things to the connection we just made 


S = Server() 
S.run() 

Merci pour votre aide.

Répondre

4

Vous avez besoin d'une forme d'E/S de socket asynchrone. Jetez un oeil à this explanation, qui traite du concept dans les termes de socket de bas niveau, et les exemples connexes qui sont implémentés en Python. Cela devrait vous orienter dans la bonne direction.

3

Pour une victoire vraiment facile vous mettre en œuvre la solution à l'aide SocketServer & la SocketServer.ThreadingMixIn

un coup d'oeil un cet exemple de serveur d'écho, il semble tout à fait semblable à ce que vous faites de toute façon: http://www.oreillynet.com/onlamp/blog/2007/12/pymotw_socketserver.html

+1

-1: SocketServer utilise un thread par connexion ce qui est une très mauvaise approche. – nosklo

+0

Oui, vous avez raison, j'ai déjà créé un serveur xmlrpc sur lequel excel a parlé via des fonctions vba qui appelaient le service xmlrpc. Cela a bien fonctionné jusqu'à ce que quelqu'un remplisse une formule autour de 1000 lignes à quel point la fonction définie a appelé mon service xmlrpc 1000 fois, créant 1000 threads. Pas drôle. L'approche tordue est certainement la voie à suivre. – Ravi

16

Mis en œuvre en twisted:

from twisted.internet.protocol import Factory, Protocol 
from twisted.internet import reactor 

class SendContent(Protocol): 
    def connectionMade(self): 
     self.transport.write(self.factory.text) 
     self.transport.loseConnection() 

class SendContentFactory(Factory): 
    protocol = SendContent 
    def __init__(self, text=None): 
     if text is None: 
      text = """Hello, how are you my friend? Feeling fine? Good!""" 
     self.text = text 

reactor.listenTCP(50000, SendContentFactory()) 
reactor.run() 

Test:

$ telnet localhost 50000 
Trying 127.0.0.1... 
Connected to localhost. 
Escape character is '^]'. 
Hello, how are you my friend? Feeling fine? Good! 
Connection closed by foreign host. 

Sérieusement, quand il s'agit d'un réseau asynchrone, c'est le chemin à parcourir. Il gère plusieurs connexions dans une approche mono-thread mono-processus.

+1

C'est parfait. Pourriez-vous donner un exemple d'envoi et de réception de données du client? Je lis sur Twisted mais c'est assez verbeux dans ses tutoriels. – SpleenTea

+1

@SpleenTea: Il suffit d'ajouter une méthode dataReceived sur le protocole et les données iront à elle. Si votre protocole est basé sur une ligne, vous voudrez peut-être sous-classer twisted.protocols.basic.LineReceiver au lieu de Protocol, donc vous pouvez simplement définir lineReceived et il sera appelé pour chaque ligne que vous obtenez du client. Pour envoyer il suffit d'utiliser self.transport.write comme je l'ai fait dans l'exemple ci-dessus. http://twistedmatrix.com/projects/core/documentation/howto/ est très utile, spécialement les tutoriels. – nosklo

1

Si vous voulez le faire en pure python (sans-twisted), vous devez faire du threading. Si vous havnt vu avant, consultez: http://heather.cs.ucdavis.edu/~matloff/Python/PyThreads.pdf

autour de la page 5/6 est un exemple qui est très pertinente;)

+0

-1: article est un article général sur les threads. Les exemples n'ont rien à voir avec les sockets. Implémenter un bon serveur de socket avec des threads est délicat, a beaucoup de détails et est difficile à déboguer, et vous n'avez aucun avantage. Il n'y a aucune raison de ne pas devenir asynchrone. – nosklo

+1

La fin de p.5 (srvr.py) est un serveur qui utilise l'interface de socket pour se connecter à un port et écouter les connexions. Tu as raison. Cela n'a rien à voir avec la discussion actuelle sur les sockets. Ma faute. – Alex

1

D'abord, acheter des livres de Comer sur TCP/IP programming.

Dans ces livres, Comer fournira plusieurs algorithmes alternatifs pour les serveurs. Il y a deux approches standard.

  • Fil par requête.

  • Processus par requête.

Vous devez choisir l'un des deux et implémenter cela. En thread-per, chaque session telnet est un fil séparé dans votre application globale.

Dans process-per, vous utilisez chaque session Telnet dans un sous-processus distinct.

Vous constaterez que le traitement par requête est beaucoup plus facile à gérer en Python et qu'il permet généralement une utilisation plus efficace de votre système. Thread-per-request est bien pour les choses qui vont et viennent rapidement (comme les requêtes HTTP).

Telnet a des sessions de longue durée où le coût de démarrage d'un sous-processus ne domine pas les performances.

+0

-1: Le Thread-per-request et le Process-per-request ne peuvent pas évoluer vers un certain nombre de requêtes simultanées. – nosklo

+2

@nosklo: "un certain nombre" Rien ne peut évoluer vers certains nombres parce qu'ils sont si énormes. Thread par demande est souvent considéré comme assez évolutif. Avez-vous des détails sur ce que sont les alternatives? –

+0

ne liez pas votre modèle de concurrence à chaque demande. Au lieu de cela, exécutez un nombre de processus/threads indépendant du nombre de requêtes. Traiter de nombreuses demandes dans le même processus/thread, de préférence en utilisant async IO. Cela va bien mieux. – nosklo

4

En retard pour la réponse, mais avec les seules réponses étant Twisted ou threads (aïe), je voulais ajouter une réponse pour MiniBoa.

http://code.google.com/p/miniboa/

Twisted est grande, mais il est une bête assez grande qui peut ne pas être la meilleure introduction à la programmation Telnet asynchrone mono-thread. MiniBoa est une implémentation légère et asynchrone de Python Telnet à un seul thread, conçue à l'origine pour les boues, ce qui correspond parfaitement à la question de l'OP.

1

Essayez le serveur MiniBoa? Il a exactement 0 dépendances, pas de choses tordues ou autres. MiniBoa est un serveur telnet async non bloquant, à un seul thread, exactement ce dont vous avez besoin.

http://code.google.com/p/miniboa/

1

Utilisez le filetage puis ajoutez le gestionnaire dans une fonction. Le fil appellera à chaque fois que je demande faite:

Regardez ce

import socket    # Import socket module 
import pygame 
import thread 
import threading,sys 

s = socket.socket()   # Create a socket object 
host = socket.gethostname() # Get local machine name 
port = 12345    # Reserve a port for your service. 
s.bind((host, port)) 
print ((host, port)) 
name = "" 
users = [] 

def connection_handler (c, addr): 
     print "conn handle" 
     a = c.recv (1024) 
     if a == "c": 
     b = c.recv (1024) 
     if a == "o": 
     c.send (str(users)) 
     a = c.recv (1024) 
     if a == "c": 
      b = c.recv (1024) 
     print a,b 






s.listen(6)     # Now wait for client connection. 
while True: 
    c, addr = s.accept() 
    print 'Connect atempt from:', addr[0] 
    username = c.recv(1024) 
    print "2" 
    if username == "END_SERVER_RUBBISH101": 
     if addr[0] == "192.168.1.68": 
     break 
    users.append(username) 
    thread.start_new_thread (connection_handler, (c, addr)) #New thread for connection 

print 
s.close() 
Questions connexes