Python客户端/服务器问题

6

我正在使用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命令的输出,但服务器出现了问题。


1
那么你的问题究竟是什么? - Sven Marnach
如何让客户端保持打开状态以输入更多命令,以便客户端可以继续输入命令并在服务器上执行它们。 - AustinM
1
你似乎在重新发明ssh,为什么? - nosklo
这并不是重新发明SSH,而只是一个基本的服务器客户端程序,客户端可以远程执行命令。虽然不具备SSH提供的安全级别,但很简单。有点像telnet。 - AustinM
1
如果你真的想了解这个,那就去看《Unix环境高级编程》第二版这本书吧……它会解释 一切 你需要开始客户端/服务器编程的内容。 - gahooa
可能是重复的问题:Python - 服务器和客户端问题 - Lennart Regebro
2个回答

6

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


我希望服务器发送一些指示命令输出已完成的内容。这个想法是服务器保持监听连接,客户端可以输入一个命令,获取其输出,然后如果用户愿意,可以输入更多的命令。 - AustinM
2
作为第三个选项,您可以让服务器在执行命令完成后生成提示符。然后客户端变得非常简单,只需来回传递字符即可。实际上,在那一点上,您已经开始重新发明telnet了。 - Greg Hewgill
嗯,第三个选项似乎是最好的。而且最简单哈哈。我应该怎么做呢?我是否需要让服务器有raw_input()函数?我有点困惑如何实现这个。 - AustinM
我明白了。所以我只需要让服务器等待命令,然后执行它,直到看到一个回车字符? - AustinM
没错。试一试吧,如果你遇到问题,可以提一个新的问题,因为这个评论区有点长了。 :) - Greg Hewgill
显示剩余5条评论

3
如果您希望客户重复执行某项操作,只需在其中添加一个循环即可;)
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())

我认为这可能更符合你的要求。

其他评论:

s.listen(1)

这个语句很可能应该移到while循环之外。你只需要调用listen一次。

pipe = os.popen(rc)

os.popen已被弃用,请使用subprocess模块代替。

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

你正在打开一个文件,但是你从未关闭这个文件。在你的sys.stdout.writelines()调用之后,应该加上file.close()
编辑:回答下面的评论;由于长度和格式问题,在此处完成
目前情况是,你只读取了一次socket,然后立即关闭了它。因此,当客户端尝试发送下一个命令时,它会看到服务器关闭了socket并指示出错。
解决方案是更改你的服务器代码,使其能够处理接收多个命令。请注意,这可以通过引入另一个循环来解决。
你需要包装
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。 - AustinM
fl = conn.makefile('w', 0) 修改为 fl = conn.makefile('w'),这样对我来说问题就解决了。将第二个参数(缓冲区大小)设置为0似乎并不太好用。 - Alex Miller
仍然没有工作。它仍然挂起 :/ 它似乎不喜欢我。http://pastie.org/1450029 我很确定我正确地包装了 while True... - AustinM
你正在运行哪个版本的Python?也许是我的客户端出了问题? - AustinM
嗯,我无法再帮助你了。我建议现在使用老式的 print() 打印方法进行调试。 - Alex Miller
显示剩余7条评论

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