服务器脚本挂起,直到我按[enter]

我使用套接字在Python中有一个基本的客户端服务器脚本。 服务器绑定到特定的端口并等待客户端连接。 当客户端连接时,它们会显示一个raw_input提示符,用于将输入的命令发送到服务器上的一个子进程并将输出返回给客户端。 有时,当我从客户端执行命令时,输出会挂起,并且不会显示raw_input提示符,直到我按[enter]键。 起初我认为这可能是一个缓冲区问题,但是当我使用带有小输出的命令时(如'clear'或'ls'等),它会发生。

客户端代码:

import os, sys
import socket
from base64 import *
import time


try:
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
except IndexError:
    print("You must specify a host IP address and port number!")
    print("usage: ./handler_client.py 192.168.1.4 4444")
    sys.exit()

socksize = 4096
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    server.connect((HOST, PORT))
    print("[+] Connection established!")
    print("[+] Type ':help' to view commands.")
except:
    print("[!] Connection error!")
    sys.exit(2)


while True:
    data = server.recv(socksize)
    cmd = raw_input(data)
    server.sendall(str(cmd))

server.close()

服务器代码:

import os,sys
import socket
import time
from subprocess import Popen,PIPE,STDOUT,call

HOST = ''                              
PORT = 4444
socksize = 4096                            
activePID = []

conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.bind((HOST, PORT))
conn.listen(5)
print("Listening on TCP port %s" % PORT)

def reaper():                              
    while activePID:                        
        pid,stat = os.waitpid(0, os.WNOHANG)     
        if not pid: break
        activePID.remove(pid)


def handler(connection):                    
    time.sleep(3)     

    while True:                                     
        cmd = connection.recv(socksize)
        proc = Popen(cmd,
              shell=True,
             stdout=PIPE,
             stderr=PIPE,
              stdin=PIPE,
              )
        stdout, stderr = proc.communicate()

        if cmd == ":killme":
            connection.close()
            sys.exit(0)

        elif proc:
            connection.send( stdout )
            connection.send("nshell => ")

    connection.close() 
    os._exit(0)


def accept():                                
    while 1:   
        global connection                                  
        connection, address = conn.accept()
        print "[!] New connection!"
        connection.send("nshell => ")
        reaper()
        childPid = os.fork()                     # forks the incoming connection and sends to conn handler
        if childPid == 0:
            handler(connection)
        else:
            activePID.append(childPid)

accept()

我看到的问题是,客户端中的最后一个循环只执行一个'server.recv(socksize)',然后调用raw_input()。 如果recv()调用没有获得服务器在该单个调用中发送的所有数据,那么它也不会收集命令输出后面的提示,因此不会显示下一个提示。 未收集的输入将位于套接字中,直到您输入下一个命令,然后收集并显示它。 (原则上,可能需要很多recv()调用才能排空套接字并进入附加提示,而不仅仅是两次调用。)

如果发生了这种情况,那么如果命令发回多个缓冲区的值(4KB)数据,或者如果它以小块间隔生成输出以便服务器端可以将数据分散到多个发送不足够快的客户端将其全部收集到一个recv()中。

为了解决这个问题,你需要客户端执行尽可能多的recv()调用,因为它需要完全耗尽套接字。 所以你需要想出一种方法让客户端知道在这个交互中服务器将发送的所有东西都被套接字排空了。 最简单的方法是让服务器将边界标记添加到数据流中,然后让客户端检查这些标记以发现何时收集了当前交互的最终数据。 有很多种方法可以做到这一点,但我可能会在服务器发送的每个数据块之前插入一个“这是以下数据块的长度”标记,并在最后发送一个长度为零的标记块。 然后,客户端主循环变为“永远:读取标记;如果标记中携带的长度为零,则中断;否则读取多个字节;”。 请注意,客户必须确保在完成标记之前recv(); 东西可以以任意大小的块形式从流套接字中出来,与发送给发送端侧套接字的写入大小完全无关。

您可以决定是将标记作为可变长度文本(带有明确的分隔符)还是作为固定长度的二进制文件发送(在这种情况下,如果客户机和服务器可以位于不同的系统上,则必须担心endian问题)。 您还可以决定客户端是否应该显示每个块(当然,您不能使用raw_input()来做到这一点),还是应该收集所有块并在最后一块之后显示整个事件已被收集。

链接地址: http://www.djcxy.com/p/9655.html

上一篇: server script hangs until I press [enter]

下一篇: completion in cmd.Cmd, when sys.stdout has been replaced