多个同时的网络连接 - Telnet服务器,Python

16

我正在用Python编写一个telnet服务器,它是一个内容服务器。用户可以通过telnet连接到该服务器,并呈现文本内容。

我的问题是,这个服务器显然需要支持多个同时连接。当前我现在拥有的实现仅支持一个连接。

这是我最初开始的基本概念验证服务器(尽管程序随着时间的推移发生了很大变化,但基本的telnet框架没有改变):

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

谢谢你的帮助。

9个回答

16

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

测试:

$ 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.

说真的,当涉及到异步网络时,twisted是首选。它可以在单线程单进程的方式下处理多个连接。


1
这很完美。你能给一个从客户端发送和接收数据的例子吗?我正在学习Twisted,但它的教程非常冗长。 - SpleenTea
1
@SpleenTea:只需在协议上添加一个dataReceived方法,数据就会传递到该方法中。如果您的协议是基于行的,则可能希望子类化twisted.protocols.basic.LineReceiver而不是Protocol,这样您就可以定义lineReceived并且每次从客户端获取一行时都会调用它。要发送数据,只需像上面的示例中一样使用self.transport.write即可。http://twistedmatrix.com/projects/core/documentation/howto/非常有用,特别是教程部分。 - nosklo

4
很抱歉回复晚了,但是由于唯一的答案是Twisted或者threads(痛苦),我想为MiniBoa添加一个答案。Twisted很好,但它是一个相当庞大的东西,可能不是单线程异步Telnet编程的最佳入门方式。MiniBoa是一个轻量级、异步单线程Python Telnet实现,最初是为mud设计的,非常适合OP的问题。请参考http://code.google.com/p/miniboa/

4
你需要一种形式的异步套接字IO。请查看这篇说明,它以低级套接字术语讨论了这个概念,并提供了用Python实现的相关示例。这应该会指引你正确的方向。

3

1
-1:SocketServer使用每个连接一个线程的方式,这是一种非常糟糕的方法。 - nosklo
是的,你说得对。我曾经创建过一个XMLRPC服务器,Excel通过调用VBA函数与之交互。它运行良好,直到有人在大约1000行填充了一个公式,导致定义的函数调用我的XMLRPC服务1000次,创建1000个线程。非常不好玩。采用扭曲的方式肯定是正确的方法。 - Ravi

2

+1:Twisted 是一个非常好的选择。实现一个简单的 Telnet 服务器应该非常容易。 - nosklo

1
使用线程,然后将处理程序添加到函数中。每次发出请求时,线程都会调用它:
看这个。
 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()

1

试试MiniBoa服务器?它完全没有任何依赖关系,不需要Twisted或其他东西。MiniBoa是一个非阻塞异步Telnet服务器,单线程,正是你所需要的。

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


1

-1: 这篇文章是关于线程的一般性文章。示例与套接字无关。使用线程实现一个好的套接字服务器很棘手,有很多细节需要考虑,且很难调试,而且你没有任何好处。没有理由不去异步化。 - nosklo
1
第5页底部(srvr.py)是一个服务器,它使用套接字接口绑定到端口并侦听连接。你说得对。它与套接字的当前讨论无关。我的错。 - Alex

1

首先,购买Comer关于TCP/IP编程的书籍。

在这些书中,Comer将为服务器提供几种可替代的算法。有两种标准方法:

  • 每个请求一个线程。

  • 每个请求一个进程。

你必须选择其中一种并实现它。

在“每个请求一个线程”中,你的整个应用程序中的每个Telnet会话都是一个单独的线程。

在“每个请求一个进程”的方法中,你将每个Telnet会话分支到一个单独的子进程中。

你会发现,“每个请求一个进程”的方式在Python中处理起来要容易得多,并且通常更有效地利用了你的系统。

对于那些快速出现和消失的东西(如HTTP请求),“每个请求一个线程”是可以的。Telnet具有长时间运行的会话,其中子进程启动成本不会影响性能。


-1:无论是每个请求一个线程还是每个请求一个进程,都无法扩展到一定数量的同时请求。 - nosklo
2
@nosklo:“一些数字”。由于它们非常巨大,因此没有任何东西可以扩展到某些数字。每个请求一个线程通常被认为是相当可扩展的。您有关于替代方案的具体信息吗? - S.Lott
不要把你的并发模型和每个请求绑定在一起。相反,运行独立于请求数量的多个进程/线程。通过使用异步IO,在同一个进程/线程中处理多个请求,这样扩展性会更好。 - nosklo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接