Pygame not working with sockets

2019-04-14 07:35发布

问题:

I created one program with pygame, imageGrab and sockets but it doesn't work. It should take a printscreen of the server with ImageGrab, convert it to a string, and send it to the client. However, the client upon receiving the information and converting it to an image raises an error:

image = pygame.image.frombuffer(img, (800,600), "RGB")
ValueError: Buffer length does not equal format and resolution size 

code Server

import sys, os, socket, pygame
from PIL import ImageGrab
from pygame.locals import *
print "streaming sever 0.1"
try:
    socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error:
    print "Error creating socket"
    sys.exit(1)

host = "127.0.0.1"
port = raw_input("Port:")

socket.bind((host, int(port)))
socket.listen(2)

socket2,client = socket.accept()
print "Client conectado: " + str(client[0]) + "\nPorta usada: " + str(client[1])


#Capture and send

while True:
    img=ImageGrab.grab().resize((800,600))
    img.tostring()
    socket2.sendall(img)

socket2.close()

code Client

import sys, os, socket, pygame
from PIL import ImageGrab
from pygame.locals import *
print "streaming client 0.1"

pygame.init()

try:
  s_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  print "streaming protocol started"
except socket.error:
  print "Error creating socket"
  sys.exit(1) 

host = "127.0.0.1"
porta = raw_input("Port:")
q_bytes = raw_input("Enter the number of MBs to transfer: ")

t_bytes = 1024*1024*int(q_bytes)

try:
  s_client.connect((host,int(porta)))
except socket.error:
  print "Error connecting to: " + host
  sys.exit(1)
print "Conectado!"



size = width, height = 800, 600
screen = pygame.display.set_mode(size)

num = 0

while True:
  img = s_client.recv(t_bytes)
  image = pygame.image.frombuffer(img, (800,600), "RGB")
  screen.blit(image,(0,0))
  pygame.display.flip()
  for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            os._exit(1)
#recebimento

s_client.close()

回答1:

The deal with sockets is you have to know exactly how long the message you're receiving is if you plan keep using the same socket connection. You seem to have some idea of this with your q_bytes = raw_input("Enter the number of MBs to transfer: "), but we need to know exactly, not to the nearest MB. Sometimes this information is sent at the front part of the data, so we can read it first and then know when to stop reading.

The exception to this is if we don't need the connection anymore; we just want this one picture. In that case, it's fine to ask for as much data as we want, we'll get an empty string back at the end.

As for the max_bytes argument to recv, that's just one maximum - there's another hardware-dependent maximum imposed on us, for my tests it was 1MB.

The code below just keeps asking for data, stops when it receives an empty string because there is no more data, then combines all this gathered data into the full string.

There are many levels of abstraction that could (and should) be built up to distance us from these complications, but the code below is just yours, working, with some irrelevant bits taken out.

Client.py

import sys, os, socket, pygame
from pygame.locals import *

pygame.init()

s_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

host = "127.0.0.1"
porta = 12350
t_bytes = 1024*1024*1

s_client.connect((host,int(porta)))
print "Conectado!"

size = width, height = 300, 500
screen = pygame.display.set_mode(size)

message = []
while True:
    s = s_client.recv(t_bytes)
    if not s:
        break
    else:
        message.append(s)
full_s = "".join(message)
print 'string received size', len(full_s)
image = pygame.image.frombuffer(full_s, size, "RGB")
#image = pygame.image.fromstring(s, size, "RGB")
screen.blit(image,(0,0))
pygame.display.flip()
for event in pygame.event.get():
    if event.type == QUIT:
        pygame.quit()
        os._exit(1)
    raw_input()

s_client.close()

Server.py

import sys, os, socket, pygame
from pygame.locals import *

socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "127.0.0.1"
port = 12350
socket.bind((host, int(port)))
socket.listen(2)
socket2,client = socket.accept()
print "Client conectado: " + str(client[0]) + "\nPorta usada: " + str(client

img = Image.open('tiny.jpg').resize((300, 500))
s = img.tostring()
print 'size of string', len(s)
socket2.sendall(s)

socket2.close()

edit: As per Mark's correction, len() calls were previously __sizeof__() method calls. __sizeof__ returns the size of the python object in bytes, not the number of bytes/characters in the string.