python 如何让服务器接受来自多个端口的连接?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1232366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 21:44:46  来源:igfitidea点击:

How to make server accepting connections from multiple ports?

pythonnetwork-programming

提问by Orjanp

How can I make a simple server(simple as in accepting a connection and print to terminal whatever is received) accept connection from multiple ports or a port range?

如何制作一个简单的服务器(就像接受连接并打印到终端一样简单)接受来自多个端口或端口范围的连接?

Do I have to use multiple threads, one for each bind call. Or is there another solution?

我是否必须使用多个线程,每个绑定调用一个线程。或者还有其他解决方案吗?

The simple server can look something like this.

简单的服务器看起来像这样。

def server():
import sys, os, socket

port = 11116
host = ''
backlog = 5 # Number of clients on wait.
buf_size = 1024

try:
    listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listening_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
    listening_socket.bind((host, port)) 
    listening_socket.listen(backlog)
except socket.error, (value, message):
    if listening_socket:
        listening_socket.close()
    print 'Could not open socket: ' + message
    sys.exit(1)

while True:
    accepted_socket, adress = listening_socket.accept()

    data = accepted_socket.recv(buf_size)
    if data:
        accepted_socket.send('Hello, and goodbye.')
    accepted_socket.close()

server()

EDIT: This is an example of how it can be done. Thanks everyone.

编辑:这是如何完成的一个例子。感谢大家。

import socket, select

def server():
import sys, os, socket

port_wan = 11111
port_mob = 11112
port_sat = 11113

sock_lst = []
host = ''
backlog = 5 # Number of clients on wait.
buf_size = 1024

try:
    for item in port_wan, port_mob, port_sat:
        sock_lst.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
        sock_lst[-1].setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
        sock_lst[-1].bind((host, item)) 
        sock_lst[-1].listen(backlog)
except socket.error, (value, message):
    if sock_lst[-1]:
        sock_lst[-1].close()
        sock_lst = sock_lst[:-1]
    print 'Could not open socket: ' + message
    sys.exit(1)

while True:
    read, write, error = select.select(sock_lst,[],[])

    for r in read:
        for item in sock_lst:
            if r == item:
                accepted_socket, adress = item.accept()

                print 'We have a connection with ', adress
                data = accepted_socket.recv(buf_size)
                if data:
                    print data
                    accepted_socket.send('Hello, and goodbye.')
                accepted_socket.close()

server()

回答by Eric Petroelje

I'm not a python guy, but the function you are interested in is "select". This will allow you to watch multiple sockets and breaks out when activity occurs on any one of them.

我不是 python 人,但您感兴趣的功能是“选择”。这将允许您观察多个套接字并在其中任何一个发生活动时中断。

Here's a python example that uses select.

这是一个使用 selectpython 示例

回答by Anon

Since Python's got so much overhead, multithreaded apps are a big point of debate. Then there's the whole blocking-operation-GIL issue too. Luckily, the Python motto of "If it seems like a big issue, someone's probably already come up with a solution (or several!)"holds true here. My favorite solution tends to be the microthread model, specifically gevent.

由于 Python 有如此多的开销,多线程应用程序是一个很大的争论点。然后还有整个阻塞操作 GIL 问题。幸运的是,Python 的座右铭“如果它看起来是一个大问题,那么有人可能已经想出了一个解决方案(或几个!)”在这里也适用。我最喜欢的解决方案往往是微线程模型,特别是gevent.

Gevent is an event-driven single-thread concurrency library that handles most issues for you out of the box via monkey-patching. gevent.monkey.patch_socket()is a function that replaces the normal socket calls with non-blocking variants, polling and sleeping to allow the switch to other greenlets as need be. If you want more control, or it's not cutting it for you, you can easily manage the switching with select and gevent's cooperative yield.

Gevent 是一个事件驱动的单线程并发库,可以通过猴子补丁为您开箱即用地处理大多数问题。gevent.monkey.patch_socket()是一个用非阻塞变体替换普通套接字调用的函数,轮询和睡眠以允许根据需要切换到其他greenlet。如果你想要更多的控制,或者它不适合你,你可以使用 select 和gevent合作的 yield轻松管理切换。

Here's a simple example.

这是一个简单的例子。

import gevent
import socket
import gevent.monkey; gevent.monkey.patch_socket()

ALL_PORTS=[i for i in xrange(1024, 2048)]
MY_ADDRESS = "127.0.0.1"    

def init_server_sock(port):
    try: 
        s=socket.socket()
        s.setblocking(0)
        s.bind((MY_ADDRESS, port))
        s.listen(5)
        return s
    except Exception, e:
        print "Exception creating socket at port %i: %s" % (port, str(e))
        return False

def interact(port, sock):
    while 1:
        try:
            csock, addr = sock.accept()
        except:
            continue
        data = ""
        while not data:
            try:
                data=csock.recv(1024)
                print data
            except:
                gevent.sleep(0) #this is the cooperative yield
        csock.send("Port %i got your message!" % port)
        csock.close()
        gevent.sleep(0)


def main():
   socks = {p:init_server_sock(p) for p in ALL_PORTS}
   greenlets = []
   for k,v in socks.items():
       if not v:
           socks.pop(k)
       else:
           greenlets.append(gevent.spawn(interact, k, v))

   #now we've got our sockets, let's start accepting
   gevent.joinall(greenlets)

That would be a super-simple, completely untested server serving plain text We got your message!on ports 1024-2048. Involving select is a little harder; you'd have to have a manager greenlet which calls select and then starts up the active ones; but that's not massively hard to implement.

那将是一个超级简单、完全未经测试的服务器,We got your message!在端口 1024-2048 上提供纯文本服务。涉及 select 有点难;你必须有一个管理器 greenlet,它调用 select 然后启动活动的;但这并不难实现。

Hope this helps! The nice part of the greenlet-based philosophy is that the select call is actually part of their hub module, as I recall, which will allow you to create a much more scalable and complex server more easily. It's pretty efficient too; there are a couple benchmarks floating around.

希望这可以帮助!我记得,基于 greenlet 的哲学的好处在于,select 调用实际上是其集线器模块的一部分,这将使您能够更轻松地创建更具可扩展性和复杂性的服务器。它也非常有效;有几个基准在浮动。

回答by Mark Rushakoff

If you really wanted to be lazy (from a programmerstandpoint, not an evaluation standpoint), you could set a timeout on your blocking read and just loop through all your sockets; if a timeout occurs, there wasn't any data available. Functionally, this is similar to what the selectis doing, but it is taking that control away from the OS and putting it in your application.

如果你真的想偷懒(从程序员的角度,而不是评估的角度),你可以在阻塞读取上设置超时,然后循环遍历所有套接字;如果发生超时,则没有任何可用数据。从功能上讲,这与select正在执行的操作类似,但它将控制权从操作系统中移开并将其放入您的应用程序中。

Of course, this implies that as your sleep time gets smaller, your program will approach 100% CPU usage, so you wouldn't use it on a production app. It's fine for a toy though.

当然,这意味着随着您的睡眠时间变短,您的程序将接近 100% 的 CPU 使用率,因此您不会在生产应用程序上使用它。不过作为玩具倒是不错。

It would go something like this: (not tested)

它会是这样的:(未测试)

def server():
    import sys, os, socket

    port = 11116
    host = ''
    backlog = 5 # Number of clients on wait.
    buf_size = 1024
    NUM_SOCKETS = 10
    START_PORT = 2000

    try:
            socket.setdefaulttimeout(0.5) # raise a socket.timeout error after a half second
            listening_sockets = []
            for i in range(NUM_SOCKETS):
                listening_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                listening_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 
                listening_socket.bind((host, START_PORT + i)) 
                listening_socket.listen(backlog)
                listening_sockets.append(listening_socket)
    except socket.error, (value, message):
            if listening_socket:
                    listening_socket.close()
            print 'Could not open socket: ' + message
            sys.exit(1)

    while True:
            for sock in listening_sockets:
                try:
                    accepted_socket, adress = sock_socket.accept()

                    data = sock.recv(buf_size)
                    if data:
                            sock_socket.send('Hello, and goodbye.')
                    sock.close()
                except socket.timeout:
                    pass

    server()