【问题标题】:Python client / server questionPython客户端/服务器问题
【发布时间】:2011-06-06 06:06:07
【问题描述】:

我正在用 python 做一个项目。我有一个客户端和一个服务器。服务器侦听连接,一旦收到连接,它就会等待来自客户端的输入。这个想法是客户端可以连接到服务器并执行系统命令,例如ls和cat。这是我的服务器代码:

import sys, os, socket


host = ''                
port = 50105

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
print("Server started on port: ", port)

s.listen(5)
print("Server listening\n")
conn, addr = s.accept()
print 'New connection from ', addr
while (1):
    rc = conn.recv(5)
    pipe = os.popen(rc)
    rl = pipe.readlines()
    file = conn.makefile('w', 0)
    file.writelines(rl[:-1])
    file.close()
    conn.close()

这是我的客户端代码:

import sys, socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = 'localhost'
port = input('Port: ')
s.connect((host, port))
cmd = raw_input('$ ')
s.send(cmd) 
file = s.makefile('r', 0)
sys.stdout.writelines(file.readlines())

当我启动服务器时,我得到了正确的输出,表明服务器正在监听。但是当我连接我的客户端并输入命令时,服务器退出并出现此错误:

Traceback (most recent call last):
File "server.py", line 21, in <module>
  rc = conn.recv(2)
File "/usr/lib/python2.6/socket.py", line 165, in _dummy
  raise error(EBADF, 'Bad file descriptor')
socket.error: [Errno 9] Bad file descriptor

在客户端,我得到了 ls 的输出,但服务器却搞砸了。

【问题讨论】:

  • 那么你的问题到底是什么?
  • 我怎样才能使客户端保持打开状态以输入更多命令。这样客户端就可以继续输入命令,并在服务器上执行。
  • 你似乎在重新发明 ssh,为什么?
  • 它并没有真正重塑 SSH,它只是一个基本的服务器客户端程序,客户端可以远程执行命令。虽然不是 SSH 提供的安全级别。很简单。有点像 telnet。
  • 如果您真的想了解这方面的知识,请阅读《Unix 环境中的高级编程》第二版...它将解释您继续前进所需的一切客户端/服务器编程。

标签: python sockets client command


【解决方案1】:

您的代码调用conn.close(),然后循环回到conn.recv(),但conn 已经关闭。

【讨论】:

  • 我将 conn.close() 从服务器中取出,现在当我在客户端输入命令时,它就会挂起。什么都没有发生。
  • 在这种情况下,客户端中的 file.readlines() 正在等待服务器发送更多内容(因为它可能会发送)。您必须做出决定——您是希望您的服务器接受一个命令然后关闭连接,还是希望服务器发送一些指示命令输出已完成的内容?如果是后者,你想寄什么?
  • 嗯,我希望服务器发送一些指示命令输出已完成的内容。这个想法是服务器继续监听连接,客户端可以输入命令,获取它的输出,然后如果用户愿意,可以输入更多命令。
  • 有两种选择 - 服务器可以继续监听新连接,即使每个单独的连接在命令完成后关闭(这有点像简单的 HTTP作品)。这具有编码更简单的优点。另一种方法是让客户端和服务器之间的连接为多个命令保持连接,但是您必须选择某种方式来标记服务器命令输出的结束,以便客户端知道何时显示下一个提示。诀窍是选择不会出现在命令输出本身的东西。
  • 作为第三个选项,您可以让 服务器 在执行完命令后生成提示。然后客户端变得非常简单,只是来回传递字符。事实上,在这一点上,你已经在重塑 telnet 的路上了。
【解决方案2】:

如果您希望您的客户重复它正在做的事情,只需在其中添加一个循环;)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = 'localhost'
port = input('Port: ')
s.connect((host, port))
while True:
    cmd = raw_input('$ ')
    s.send(cmd) 
    file = s.makefile('r', 0)
    sys.stdout.writelines(file.readlines())

应该更接近你想要的。

其他cmets:

s.listen(1)

这个语句可能应该移到 while 循环之外。您只需拨打listen 一次。

pipe = os.popen(rc)

os.popen 已被弃用,请改用subprocess module

file = s.makefile('r', 0)

您正在打开一个文件,但您从未关闭该文件。您可能应该在您的sys.stdout.writelines() 调用之后添加一个file.close()

编辑: 回答以下评论;由于长度和格式,在这里完成

就目前而言,您从套接字读取一次,然后立即关闭它。因此,当客户端去发送下一个命令时,它看到服务器关闭了套接字并指示错误。

解决方案是更改您的服务器代码,以便它可以处理接收多个命令。请注意,这是通过引入另一个循环来解决的。

你需要包装

rc = conn.recv(2)
pipe = os.popen(rc)
rl = pipe.readlines()
fl = conn.makefile('w', 0)
fl.writelines(rl[:-1])

在另一个 while True: 循环中重复,直到客户端断开连接,然后将其包装在一个 try-except 块中,该块捕获客户端断开连接时 conn.recv() 抛出的 IOError。

try-except 块应该是这样的

try:
    # the described above loop goes here
except IOError:
    conn.close()
# execution continues on...

【讨论】:

  • 这个循环很好,因为一旦输入命令,它就会在服务器机器上执行,我在客户端机器上得到输出,然后我再次得到'$'提示。问题是,在那之后,当我输入另一个命令时,什么也没有发生。然后,如果我尝试另一个命令,则会收到错误 socket.error: [Errno 32] Broken pipe
  • 嗯,很有趣。现在客户端只是挂起,在服务器关闭之前我没有得到命令的输出。
  • fl = conn.makefile('w', 0) 更改为 fl = conn.makefile('w') 为我修复了它。将第二个参数(缓冲区大小)设置为 0 似乎效果不佳。
  • 我还必须在rc = conn.recv(2) 之后添加if len(rc) == 0: raise IOError,因为对我来说,当在另一端关闭的套接字上调用recv 时,它什么也没返回。我认为它应该引发 IOError,但我想它不会? (如果其他人可以跳到这里并解释原因,将不胜感激。)
  • 好吧,我已经完成了建议的所有操作,但我仍然遇到同样的问题 -_- 它仍然挂起,直到服务器关闭。只是为了确保这是我的服务器/客户端:pastie.org/1447201
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 2016-03-30
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多