Есть ли способ получить getpeername () в Python после отключения сокета? - PullRequest
0 голосов
/ 09 декабря 2018

У меня есть простой сервер, подобный этому:

#!/usr/bin/env python2

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 50000))
server.listen(5)

# Set TCP Keepalive
server.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
server.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)

connection, client_address = server.accept()

while True:
    try:
        data = connection.recv(1024)
    except socket.error:
        print "%s dropped out" % (connection.getpeername(),)
        break

    if data:
        print "%s: %s" % (connection.getpeername(), data)
    else:
        print "%s disconnected" % (connection.getpeername(),)
        break

Я использую TCP Keepalive, чтобы выяснить, исчезает ли клиент (а не правильно отключается).

Проблема, с которой я сталкиваюсьв том, что когда клиент действительно исчезает, и я ловлю исключение socket.error, моя инструкция print пытается выяснить, какой клиент отключен, с помощью connection.getpeername (), который больше не работает.Он генерирует исключение

socket.error: [Errno 107] Transport endpoint is not connected

.

В этом простом примере есть только один клиент, который может подключаться одновременно, но с более сложным кодом, который обрабатывает несколько клиентов одновременно, онможет быть полезно узнать, какой клиент отключился.

После того, как сокет отсоединился, нет ли способа узнать, к какой конечной точке он был ранее подключен?Почему объект connection не сохраняет информацию об IP-адресе / порте клиента в connection.getpeername() даже после отключения клиента?Это потому, что объект connection потенциально можно использовать повторно?

Как я могу заставить мой код работать на самом деле и распечатать, какой клиент пропал?Нужно ли сохранять вывод connection.getpeername() в переменной, чтобы я мог вспомнить его позже?Это означало бы, что если у меня несколько клиентов, мне нужно было бы хранить значение connection.getpeername() для каждого клиента в списке?

Я знаю, что использование socket - довольно низкий уровень, и я уверен, чтов действительности проще было бы использовать SocketServer.TCPServer - я просто пытаюсь понять, что такое низкоуровневая сеть.

1 Ответ

0 голосов
/ 10 декабря 2018

Это, к сожалению, ограничение интерфейса ОС.Бинарная (C) программа также не сможет получить имя партнера после того, как известно, что сокет отключен.Это своего рода несчастная бородавка.Вы не можете повторно использовать сокет для нового соединения, поэтому нет веской причины, по которой я могу подумать, почему не удалось получить имя пира, но ... это невозможно.

Первая идея состоит в том, чтобы использовать отдельный dict с объектом сокета, служащим ключом, через который вы будете искать связанный адрес клиента:

saved_addr = {}
...
connection, client_address = server.accept()
saved_addr[connection] = client_address`
while True:
    try:
        data = connection.recv(1024)
    except socket.error:
        print("{} disconnected".format(saved_addr[connection]))

Второй - создатьотдельный класс, чтобы обернуть сокет.Класс может сохранить адрес партнера, который вы получили от accept, и вернуть его вам при необходимости.Между тем, он может прозрачно делегировать все остальные операции нижележащему сокету.

Демонстрация:

import socket
import time

class ConnectedSocket:
    def __init__(self, sock, peer):
        self.sock = sock
        self.peer = peer
    def get_peer(self):
        return self.peer
    def __getattr__(self, name):
        return getattr(self.sock, name)

def run_server(port):
    server = socket.socket()
    server.bind(('', port))
    server.listen(5)

    while True:
        csock, addr = server.accept()
        csock = ConnectedSocket(csock, addr)
        print("Accepted from {}".format(addr))

        while True:
            time.sleep(.5)
            try:
                csock.send(b'Hello')
            except socket.error as err:
                print("Error {} sending to peer {}".format(err, csock.get_peer()))
                csock.close()
                break

run_server(9999)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...