Python - [Errno 10054] 远程主机强行关闭现有连接 - 发送腌制数据问题 [英] Python - [Errno 10054] An existing connection was forcibly closed by the remote host - sending pickled data issues

查看:43
本文介绍了Python - [Errno 10054] 远程主机强行关闭现有连接 - 发送腌制数据问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遵循了一个在线教程,该教程展示了如何制作一个石头剪刀布游戏,该游戏可以处理连接到服务器的多个客户端,从而允许多人游戏.游戏运行正常,所以我尝试对不同的游戏使用相同的网络逻辑,但我在那里遇到了一些错误.特别是在使用 send 函数时:使用 send 函数时,我得到一个 EOFError: Ran out of input 错误.在这篇文章中 在线游戏给出错误("ConnectionAbortedError: [WinError 10053] " 我发现了一个假设的修复程序,即发送函数会等待直到它接收到所有数据,因为它有时在接收腌制数据时可能会出现问题.

I followed an online tutorial that showed how to make a rock paper scissors game that can handle multiple clients connecting to a server, allowing multiplayer. The game works so I tried to use the same network logic for a different game but I ran into some errors there. Particularly when using the send function: Using the send function I get an EOFError: Ran out of input error. In this post Online game give the error "("ConnectionAbortedError: [WinError 10053] " I found a supposed fix where the send function waits until it has received all of the data, as it sometimes may have issues when receiving pickled data.

但是,当我用新的 send2 函数(临时名称)替换 send 函数时,我遇到了不同的错误.即:[Errno 10054] 一个现有的连接被远程主机强行关闭.

However, when I replace the send function with the new send2 function (temporary name) I run into different errors. Namely: [Errno 10054] An existing connection was forcibly closed by the remote host.

已解决,请参阅以下段落:

SOLVED, SEE FOLLOWING PARAGRAPh:

本质上,我想要做的是用更好的发送函数替换我的发送函数(它也接收数据),该函数发送腌制数据并接收确切数量的发送信息.

Essentially, what I'm trying to do is replacing my send function (which also receives data) by a better send function that sends pickled data and receives the exact amount of sent information.

def send(self, data):
    try:
        self.client.send(pickle.dumps(data))
        return pickle.loads(self.client.recv(2048))
    except socket.error as e:
        print(e)

# source: https://stackoverflow.com/questions/62361900/online-game-give-the-error-connectionabortederror-winerror-10053
def send2(self, data):
    data_to_send = pickle.dumps(data)
    data_size = struct.pack( '!I', len(data_to_send))
    try:
        self.client.send(data_size)
        self.client.send(pickle.dumps(data))
        
        package = self.receive()
        return package
    except socket.error as e:
        print(e)

        
def receive(self):
    packet = None
    buffer = bytes()
    expected = -1
    print("receiving stuff")
    while len(buffer) < 4:
        print("received a buffer")
        try:
            partial_data = self.client.recv(4 - len(buffer))
            if partial_data:
                 buffer.append(partial_data)
                 if len(buffer) == 4:
                     expected = struct.unpack('!I', buffer)
        except Exception as e:
            print(e)
            break

    # If we received a buffer size, try to receive the buffer
    if expected > 0:
        buffer = bytes()
        while len(buffer) < expected: 
            try:
                partial_data = self.client.recv(expected - len(buffer))
                if partial_data:
                     buffer.append(partial_data)
                     # Have we received the full data-set yet?
                     if len(buffer) == expected:
                         packet = pickle.loads(buffer)
            except:
                break
            
    print(packet)
    return packet

我假设我忽略了一些非常简单的事情.如果需要,我可以发布运行游戏的 4 个 .py 文件.我所需要的只是创建一个更好的 send 函数,当它处理发送有时不值得泡菜的数据时,它不会使客户端崩溃.

I'm assuming I'm overlooking something incredibly easy. If needed, I can post the 4 .py files that run the game. All I need is to create a better send function that doesn't crash the client when it is dealing with sending data that is sometimes not pickle-worthy.

Server.py 代码

Server.py code

import socket
import pickle
from _thread import *
import sys
sys.path.append("C:/Program Files/Hero Realms/Multiplayer/")
from game import Game
from network import Network

server = "" # this is defined but I removed it for this page
port = 5555

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

try:
    s.bind((server, port))
except socket.error as e:
    print(e)
    
s.listen(2)
print("Waiting for a connection, Server Started")

connected = set()
games = {}
id_count = 0

def threadedClient(conn, p, game_id):
    global id_count 
    conn.send(str.encode(str(p)))
    
    reply = ""
    while True:
        try:
            #data = pickle.loads(conn.recv(4096))
            data = Network.receive(s)
            print(data)
            # check if the game still exists
            if game_id in games:
                game = games[game_id]
                
                if not data:
                    break
                else:
                    if data == "reset":
                        game.resetWent()
                    elif data != "get":
                        game.play(p, data)
                        
                    reply = game
                    conn.sendall(pickle.dumps(reply))
                    
            else:
                break
        except:
            break
        
    print("Lost connection")
    
    try:
        print("Closing game", game_id)
        del games[game_id]
    except:
        pass
    
    id_count -=1
    conn.close()
    
    
while True:
    conn, addr = s.accept()
    print("Connected to: ", addr)
    
    id_count += 1
    p = 0
    game_id = (id_count - 1) // 2
    
    if id_count % 2 == 1:
        games[game_id] = Game(game_id)
        print("Creating a new game...")
    else:
        games[game_id].ready = True
        p = 1
        
    
    start_new_thread(threadedClient, (conn, p, game_id))

客户端.py

import pygame

import sys
sys.path.append("C:/Program Files/Hero Realms/Multiplayer/")
from network import Network
import pickle

pygame.font.init()
pygame.init()

width = 700
height = 700
win = pygame.display.set_mode((width, height))
pygame.display.set_caption("Client")

class Button:
    def __init__(self, text, x, y, color):
        self.text = text
        self.x = x
        self.y = y
        self.color = color
        self.width = 150
        self.height = 100
        
    def draw(self, win):
        pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height))
        font = pygame.font.SysFont("comicsans", 40)
        text = font.render(self.text, 1, (255, 255, 255))
        win.blit(text, (self.x + round(self.width / 2) - round(text.get_width() / 2), self.y + round(self.height / 2) - round(text.get_height() / 2)))
        
    def click(self, pos):
        x1 = pos[0]
        y1 = pos[1]
        
        if self.x <= x1 <= self.x + self.width and self.y <= y1 <= self.y + self.height:
            return True
        else:
            return False
        
def redrawWindow(win, game, p):
    win.fill((128, 128, 128))
    
    if not(game.connected()):
        font = pygame.font.SysFont("comicsans", 80)
        text = font.render("Waiting for Player...", 1, (255, 0, 0), True)
        win.blit(text, (width / 2 - text.get_width() / 2, height / 2 - text.get_height() / 2))
    else:
        font = pygame.font.SysFont("comicsans", 60)
        text = font.render("Your Move", 1, (0, 255, 255))
        win.blit(text, (80, 200))
        
        text = font.render("Opponents", 1, (0, 255, 255))
        win.blit(text, (380, 200))
        
        move1 = game.getPlayersMove(0)
        move2 = game.getPlayersMove(1)
        if game.bothWent():
            text1 = font.render(move1, 1, (0, 0, 0))
            text2 = font.render(move2, 1, (0, 0, 0))
        else:
            if game.p1_went and p == 0:
                text1 = font.render(move1, 1, (0, 0, 0))
            elif game.p1_went:
                text1 = font.render("Locked in", 1, (0, 0, 0))
            else:
                text1 = font.render("Waiting...", 1, (0, 0, 0))
                
            if game.p2_went and p == 1:
                text2 = font.render(move2, 1, (0, 0, 0))
            elif game.p2_went:
                text2 = font.render("Locked in", 1, (0, 0, 0))
            else:
                text2 = font.render("Waiting...", 1, (0, 0, 0))
                
        if p == 1:
            win.blit(text2, (100, 350))
            win.blit(text1, (400, 350))
        else:
            win.blit(text1, (100, 350))
            win.blit(text2, (400, 350))
                
        for btn in btns:
            btn.draw(win)
            
    pygame.display.update()
        
        
        

    
    
    
btns = [Button("Rock", 50, 500, (0, 0, 0)), Button("Scissors",  250, 500, (255, 0, 0)), Button("Paper", 450, 500, (0, 255, 0))]


def main():
    run = True
    clock = pygame.time.Clock()
    n = Network()
    player = int(n.getP())
    print("You are player ", player)
    
    while run:
        clock.tick(60)
        try:
            game = n.send2("get")
        except:
            run = False
            print("Couldn't get game")
            break
        
        if game.bothWent():
            redrawWindow(win, game, player)
            pygame.time.delay(500)
            try:
                game = n.send2("reset")
            except:
                run = False
                print("Couldn't get game")
                break
            
            font = pygame.font.SysFont("comicsans", 90)
            if (game.winner() == 1 and player == 1) or (game.winner() == 0 and player == 0):
                text = font.render("You won!", 1, (255, 0, 0))
            elif game.winner() == -1:
                text = font.render("Tie game!", 1, (255, 0, 0))
            else:
                text = font.render("You lost", 1, (255, 0, 0))
                
            win.blit(text, (width / 2 - text.get_width() / 2, height / 2 - text.get_height() / 2))
            pygame.display.update()
            pygame.time.delay(2000)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.quit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                for btn in btns:
                    if btn.click(pos) and game.connected():
                        if player == 0:
                            if not game.p1_went:
                                n.send2(btn.text)
                        else:
                            if not game.p2_went:
                                n.send2(btn.text)
                                
        redrawWindow(win, game, player)
                        
def menuScreen():
    run = True
    clock = pygame.time.Clock()
    
    while run:
        clock.tick(60)
        win.fill((128, 128, 128))
        font = pygame.font.SysFont("comicsans", 60)
        text = font.render("Click to play!", 1, (255, 0, 0))
        win.blit(text, (100, 200))
        pygame.display.update()
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                run = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                run = False
                
    main()
    
while True:
    menuScreen()

推荐答案

我终于完成了我最初想做的事情.我想编写一个发送函数,发送 X 数量的腌制数据,同时接收函数接收 X 数量的腌制数据,而不会在收到太小而无法腌制的数据时抛出错误.

I managed to finally accomplish what I wanted to do in the first place. I wanted to write a send function that sent out X amount of pickled data alongside a receive function that received X amount of pickled data without throwing errors when it received data that was too small to be pickled.

请参阅 send_datareceive_data 函数以了解 network.py 中的解决方案.服务器文件 server.py 也需要这些函数,尽管略有不同.因此,在您的客户端文件中,您应该使用 network.send_data 函数将数据从客户端发送到服务器.

See the send_data and the receive_data functions for the solution in network.py. The server file server.py requires these functions as well although slightly different. In your client file you should thus make use of the network.send_data function to send data from the client to the server.

network.py

import socket
import pickle
HEADERSIZE = 10

class Network:
    def __init__(self):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server = "enter server address"
        self.port = 5555
        self.addr = (self.server, self.port)
        self.p = self.connect()
        
    def getP(self):
        return self.p
    
    def connect(self):
        try:
            self.client.connect(self.addr)
            return self.client.recv(2048).decode()
        except:
            pass

    # works but only sends and receives string encoded data without a buffer
    def send1(self, data):
        try:
            self.client.send(str.encode(data))
            return pickle.loads(self.client.recv(2048))
        except socket.error as e:
            print(e)
            
    # works but only sends and receives pickled data without a buffer
    def send2(self, data):
        try:
            self.client.send(pickle.dumps(data))
            return pickle.loads(self.client.recv(2048))
        except socket.error as e:
            print(e)
        
    # this is the function you should use, it uses a receive function that has a buffer
    # and ensures that you receive ALL the information that you sent without throwing errors
    def send_data(self, data):
        data_to_send = pickle.dumps(data)
        data_size = bytes(f'{len(data_to_send):<{10}}', "utf-8")
        try:
            self.client.send(data_size + data_to_send)
            
            package = self.receive_data()
            return package
        except socket.error as e:
            print(e)
    
    def receive_data(self):
        full_msg = b''
        new_msg = True
        while True:
            msg = self.client.recv(16)
            if new_msg:
                msglen = int(msg[:HEADERSIZE])
                new_msg = False
                
            full_msg += msg
    
            if len(full_msg)-HEADERSIZE == msglen:
                data = pickle.loads(full_msg[HEADERSIZE:])
                break
    
        return data

server.py

import socket
import pickle
from _thread import *
import sys
from game import Game

server = "enter server address"
port = 5555

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

try:
    s.bind((server, port))
except socket.error as e:
    print(e)
    
s.listen(2)
print("Waiting for a connection, Server Started")

connected = set()
games = {}
id_count = 0
HEADERSIZE = 10

def receive_data(sock):
    full_msg = b''
    new_msg = True
    while True:
        msg = sock.recv(16)
        if new_msg:
            msglen = int(msg[:HEADERSIZE])
            new_msg = False

        full_msg += msg

        if len(full_msg)-HEADERSIZE == msglen:
            data = pickle.loads(full_msg[HEADERSIZE:])
            break

    return data

def send_data(clientsocket, data):
    data_to_send = pickle.dumps(data)
    data_size = bytes(f'{len(data_to_send):<{10}}', "utf-8")
    try:
        clientsocket.send(data_size + data_to_send)
        
    except socket.error as e:
        print(e)

def threadedClient(conn, p, game_id):
    global id_count 
    conn.send(str.encode(str(p)))
    
    reply = ""
    while True:
        try:
            data = receive_data(conn)
            # check if the game still exists
            if game_id in games:
                game = games[game_id]
                
                if not data:
                    break
                else:
                    if data == "reset":
                        game.resetWent()
                    elif data != "get":
                        game.play(p, data)
                        
                    reply = game
                    
                    send_data(conn, reply)

            else:
                break
        except Exception as e:
            print("Failed try")
            print(e)
            break
        
    print("Lost connection")
    
    try:
        print("Closing game", game_id)
        del games[game_id]
    except:
        pass
    
    id_count -=1
    conn.close()
    
    
while True:
    conn, addr = s.accept()
    print("Connected to: ", addr)
    
    id_count += 1
    p = 0
    game_id = (id_count - 1) // 2
    
    if id_count % 2 == 1:
        games[game_id] = Game(game_id)
        print("Creating a new game...")
    else:
        games[game_id].ready = True
        p = 1
        
    
    start_new_thread(threadedClient, (conn, p, game_id))

这篇关于Python - [Errno 10054] 远程主机强行关闭现有连接 - 发送腌制数据问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆