Klient, który powinien uruchomić serwer gdy ten nie działa, zawiesza się

0

Dzień dobry.

Mam, po wielu trudach serwer, który się włącza nawet wtedy, gdy wcześniej został zamknięty przez CRTL+Z (Linux). Klient powinien go uruchomić, jeżeli stwierdzi, że nie działa.

Niestety, tak się nie dzieje. Proszę o wskazówkę gdzie może być problem. Klient nawet nie printuje gdy kod printów jest na samej górze kodu. Nie wiem czemu, bo jak serwer działa, to wszystkie printy testowe działają.

Załączam kod:

#!/usr/bin/python3

#KLIENT

import socket
import sys
import os
import time

print("000", end="")

HOST = ''       # The remote host
PORT = 50007    # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("a", end="")
while 1:
    try:
        s.connect((HOST, PORT))
        print("!", end="")
        break
    except Exception as e:
        os.fork()
        os.system("nohup python3 ./serv.py")
        time.sleep(2)
param = ""
if 1 < len(sys.argv):
    param = str(sys.argv[1])
s.send(param.encode())
data = s.recv(1024).decode()
s.close()
print(data, end='')
#!/usr/bin/python3
# SERWER

import sys
import socket
import subprocess
import re
import os, signal
import time

HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
while 1:
    try:
        s.bind((HOST, PORT))
        break
    except Exception as e:
        p = subprocess.Popen("lsof -w | grep serv.py | grep IPv4 | grep LISTEN | awk '{print $2}'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        output, error = p.communicate()
        pid = int(re.sub("\n", "", output.decode()))
        os.kill(pid, signal.SIGKILL)
        time.sleep(1)
s.listen(1)
while 1:
    conn, addr = s.accept()
    data = conn.recv(1024).decode()
    if not data:
        conn.close()
        break
    try:
        conn.send((data + "moje dodatki").encode())
    except Exception as e:
        conn.send(str(e).replace("\n", " ").encode())
    conn.close()

Dzięki
M.

0

Mam, po wielu trudach serwer, który się włącza nawet wtedy, gdy wcześniej został zamknięty przez CRTL+Z (Linux).

Już Ci ktoś napisał, ale dalej uparcie twierdzisz swoje, CRTL+Z nie zamyka procesów. On je usypia.

0

@szatkus: Dziękuję, wersja na Django działa. Jednak jeżeli ktoś z Was umiałby pomóc w wersji na sockecie to proszę o pomoc. Dzięki!

1

Systemd by rozwiązał problem. Do tego służy. Tworzysz 2 unity, jedne wymagany przez drugi. W ten sposób masz kod niezależny od platformy, a na poziomie platformy zdefiniowane zależności potrzebne to wystartowania serwisów.

Co do samego printa, "000" utknęło w buforze po stronie jądra. Jakbyś zrobił flush to by wypisało (print("000", end="", flush=True)).

0

Dziękuję. Właśnie czytam o tym systemd. Spróbuję to ogarnąć. Meanwhile czy mógłbyś sypnąć jakimś przydatnym linkiem, bo to co znalazłem średnio rozumiem ;P

0

I ten fork to po co, skoro wołasz os.system("nohup python3 ./serv.py")

2

Przede wszystkim to należy go (fork) poprawnie używać. Fork tworzy kopię obecnego procesu. Zwraca wartość, która służy do identyfikacji czy jest się w kopi procesu, czy w oryginalnym procesie. https://docs.python.org/3/library/os.html#os.fork

Wołanie fork w pętli, tak jak w tym kodzie, skutkuje tworzeniem kopii procesów w nieskończoność.

0

No dobra, a jak sprawdzić, czy dany proces serwera, jest uśpiony czy jest nowym procesem?

0

jest we mnie coś głębokiego na "nie" do takiej architektury - pisałem w poprzedni poście @mpaw n/t

A teraz dociera do mnie, że jeszcze django do tego. Żaden serwer / framework webowy nie lubi się z obcymi procesami.
Toż to zamotanie jak twory @zku...i w C/C++

Po cholerę w ogóle te forki i procesy? Nie widzę tu ŻADNEGO uzasadnienia.
Potem autor dzielnie rozwiązuje problemy nieznane innym.

Python jest tak skutecznym językiem, że da się wsztsko napisać in-place i to zwięzłe

0

Chyba już doszedłem czemu nie działa ten kod. Po prostu, zabijać można tylko procesy spokrewnione. Jak uruchamiam serwer ręcznie i go zawieszam, to nie mogę go zabić innym procesem. Mogę to zrobić tylko z konsoli. Tylko teraz nie wiem, jak to obejść. Proszę o wskazówki jak to ominąć, dzięki.

M.

2

Widzę, że brniemy dalej. To nie ma sensu. Ten serwer powinien byc wystartowany przez nginx czy tam apache.

Jak chcesz by działało jednak jako dwa procesy, jeden jako serwis w tle, to można to zrobić przy pomocy systemd w ten sposób:

mpaw.service:

[Unit]
Description=Mpaw service
After=network.target mpaw.socket
Requires=mpaw.socket

[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/mpaw/server.py
TimeoutStopSec=5

[Install]
WantedBy=multi-user.target

mpaw.socket

[Unit]
Description=Mpaw Socket
PartOf=mpaw.service

[Socket]
ListenStream=127.0.0.1:50007

[Install]
WantedBy=sockets.target

client.py:

#!/usr/bin/env python3

import socket
import sys
import os

HOST = ''
PORT = 50007

if len(sys.argv) <= 1:
    sys.exit(1)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as conn:
    
    # Starts the systemd socket if not started. 
    # Socket will activate the service on ingresss traffic.
    os.system("systemctl start mpaw.socket")

    conn.connect((HOST, PORT))
    conn.send(sys.argv[1].encode())
    data = conn.recv(1024).decode()

    print(data)


server.py:

#!/usr/bin/env python3

import socket    
import socketserver

SD_LISTEN_FDS_START = 3

class SystemdActivatedTCPServer(socketserver.TCPServer):
    def __init__(self, handler):
        # Instead of creating a socket, use file descriptor 
        # of systemd allocated socket.
        socketserver.TCPServer.__init__(self, None, handler, bind_and_activate=False)
        self.socket = socket.fromfd(SD_LISTEN_FDS_START, self.address_family, self.socket_type)

class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024).strip().decode("utf-8")
        data = "ECHO: " + data
        self.request.sendall(data.encode("utf-8"))

if __name__ == "__main__":
    with SystemdActivatedTCPServer(Handler) as server:
        server.serve_forever()

Uruchomienie:

cp mpaw.socket /etc/systemd/system
cp mpaw.service /etc/systemd/system
mkdir /opt/mpaw/
cp server.py /opt/mpaw/server.py
systemctl start mpaw
systemctl status mpaw
journalctl -u mpaw

Ręczne klepanie zarządzania procesami, które semantycznie są serwisami lub deamonami, mija się z celem. Mnóstwo błędów i nieobsłużonych sytuacji w implementacji i brak zrozumienia jak te mechanizmy działają.

Osobną kwestią pozostaje to czy w taki sposób możesz używać połączenia TCP, które z natury jest strumieniowe i nie zachowuje granicy wysłanych danych. Moim zdaniem nie, dlatego powinno to być na http, rpc albo przynajmniej własnym protokole nad tcp, który jasno określa gdzie kończy i zaczyna się wiadomość (nawet głupi znak końca linii jako koniec wiadomości).

1 użytkowników online, w tym zalogowanych: 0, gości: 1