如何创建一个简单的服务器(简单如接受连接并打印到终端接收的任何内容)接受来自多个端口或端口范围的连接?
我是否必须使用多个线程,每个绑定调用一个。还是有另一种解决方案吗?
简单的服务器看起来像这样。
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()
编辑: 这是一个如何完成它的例子。谢谢大家。
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()
答案 0 :(得分:7)
我不是蟒蛇人,但你感兴趣的功能是“选择”。这将允许您观察多个套接字,并在其中任何一个发生活动时发生。
答案 1 :(得分:3)
由于Python的开销很大,多线程应用程序是一个很大的争论点。然后还有整个阻塞操作-GIL问题。幸运的是,的Python座右铭“如果它看起来像一个大问题,有人可能已经提出了一个解决方案(或几个!)”在这里成立。我最喜欢的解决方案往往是微线程模型,特别是gevent
。
Gevent是一个事件驱动的单线程并发库,可以通过猴子修补程序为您解决大多数问题。 gevent.monkey.patch_socket()
是一个函数,用非阻塞变量替换正常的套接字调用,轮询和休眠以允许根据需要切换到其他greenlet。如果您想要更多控制权,或者不想为您削减控制权,您可以使用select和gevent
的合作收益率轻松管理切换。
这是一个简单的例子。
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)
这将是一个超级简单,完全未经测试的服务器,在端口1024-2048上提供纯文本We got your message!
。参与选择有点困难;你必须有一个经理greenlet调用select然后启动活动的;但这并不是很难实现的。
希望这有帮助!基于greenlet的哲学的一个很好的部分是,select调用实际上是它的hub模块的一部分,我记得,这将允许你更容易地创建一个更加可扩展和复杂的服务器。它也很有效率;有几个基准浮动。
答案 2 :(得分:2)
如果你真的想要懒惰(从程序员的角度来看,而不是评估的立场),你可以设置阻塞读取的超时并循环遍历所有套接字;如果发生超时,则没有任何可用数据。从功能上讲,这类似于select
正在做的事情,但是它将控制权从操作系统中移除并将其放入您的应用程序中。
当然,这意味着随着您的睡眠时间变小,您的程序将达到100%的CPU使用率,因此您不会在生产应用程序上使用它。虽然这对玩具来说很好。
它会是这样的:(未经测试)
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()