websocket Modul für einfache Verbindung?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Hi,

ich würde gerne über websocket mit einer API kommunizieren. Dies würde ich möglichst unkompliziert und leicht haben, damit es auch wirklich fehlerfrei funktioniert und ich nicht irgendwas falsch mache/vergesse.
Für normale REST APIs gibt es ja das tolle Modul "requests" womit man viele vergleichsweise komplizierte Schritte sparen kann und es daher recht leicht ist, API calls zu machen.

Nun ist meine Frage, ob es so ein Allrounder Modul auch für websockets bzw socket.io gibt?
Habe mittlerweile schon hören müssen, dass websocket nicht gleich websocket ist. Da gibt es wohl welche wo ein einfacher pusherclient ausreicht, dann etwas fortgeschrittenere die mit socket.io arbeiten und dann noch deutlich kompliziertere. Mir wurde schon von einem Nicht-Python-Programmierer gesagt, dass es da vermutlich nichts ähnliches wie requests für geben wird.

Damit es einfacher wird, nehmen wir mal ein Beispiel.
Die Dokumentation ist nur im eingeloggten Zustand einsehbar, deswegen hier 2 screenshots der sehr kurzen Dokumentation:
http://www2.pic-upload.de/img/29897251/ ... e_ws_1.jpg
http://www2.pic-upload.de/img/29897255/ ... e_ws_2.jpg

Alle Angaben die ich bekomme sind also Socket.io, Host, Port und event. Was muss ich nun tun?
Ist das hier vllt genau das Modul was ich suche? https://pypi.python.org/pypi/socketIO-client
Leider ist die Dokumentation ziemlich sparsam, oder gibts da noch eine bessere?

Soweit ich verstanden habe, heißt Port 443 nichts anderes, als dass es eine https, also SSL Verbindung ist.
Also habe ich mal versucht das Beispiel für ein Event und das für die SSL Verbindung zu kombinieren und gucken was passiert:

Code: Alles auswählen

from socketIO_client import SocketIO

def on_remove_order_response(*args):
    print('on_remove_order_response', args)

if __name__ == '__main__':
    socketIO = SocketIO('https://ws.bitcoin.de',verify=False)
    socketIO.on('remove_order_response', on_remove_order_response)
    socketIO.emit('remove_order')
    socketIO.wait(seconds=1)
Aber schon in der Zeile "socketIO = ..." tritt immer ein Fehler auf.

Code: Alles auswählen

C:\Python34\lib\site-packages\requests\packages\urllib3\connectionpool.py:791: InsecureRequestWarning: Unverified HTTPS
request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.org/en/latest/security.html
  InsecureRequestWarning)

Traceback (most recent call last):
  File "bitcoinde_ws.py", line 16, in <module>
    socketIO = SocketIO('https://ws.bitcoin.de'verify=False)
  File "C:\Python34\lib\site-packages\socketIO_client\__init__.py", line 331, in __init__
    resource, hurry_interval_in_seconds, **kw)
  File "C:\Python34\lib\site-packages\socketIO_client\__init__.py", line 51, in __init__
    self._transport
  File "C:\Python34\lib\site-packages\socketIO_client\__init__.py", line 59, in _transport
    self._engineIO_session = self._get_engineIO_session()
  File "C:\Python34\lib\site-packages\socketIO_client\__init__.py", line 73, in _get_engineIO_session
    transport.recv_packet())
StopIteration
Auch ohne die obige Warnung, kam bei vorherigen Versuchen ohne SSL immer der StopIteration Fehler. Also weiß ich nicht, ob die Zusammenhängen oder nicht.

Also meine Fragen nun an euch:
Bin ich zumindest auf dem richtigen Weg? Oder ist das Modul eh nutzlos für meinen Zweck?
Übernimmt das Modul dann alle wichtigen Aufgaben wie Heartbeat, reconnect und was es da sonst noch alles zu beachten gibt?

edit:
wenn ich dem Link https://urllib3.readthedocs.org/en/latest/security.html folge, wird mir da erklärt, dass SSL out of the box nicht geht und ich noch was tun muss. Aber danach werden soviele unterscheidliche Dinge aufgelistet, dass ich nicht weiß, was ich davon nun genau tun muss. Aktuell teste ich das skirpt auf meinem windows 8 pc. Aber letzlich laufen soll es auf Debian 8.1 x64 .
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Die beiden Namen websockets und socket.io existieren nicht nur weil irgendjemandem langweilig war, sie bezeichnen tatsächlich unterschiedliche Dinge. Bevor du irgendwas ausprobierst solltest du dir also erstmal klar um was es geht: websockets oder socket.io?
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

DasIch hat geschrieben:Die beiden Namen websockets und socket.io existieren nicht nur weil irgendjemandem langweilig war, sie bezeichnen tatsächlich unterschiedliche Dinge. Bevor du irgendwas ausprobierst solltest du dir also erstmal klar um was es geht: websockets oder socket.io?
Die Seiten auf denen ich via "websocket" eine Verbindung aufbauen will, nennen es alle "websocket" egal ob es über pusherclient oder socket.io oder sonstwas gemacht wird. Deswegen meine Formulierungen ;)

In meinem Beispiel um das es jetzt erstmal gehen soll, geht es um socket.io.
Siehe Screenshots. Da steht, dass es über socket.io gemacht wird.

edit: zumindest dachte ich es steht da... warte ich schau nochmal nach :D
Ja genau, im 2ten Screenshot steht ein Beispiel in Javascript, welches mit socket.io arbeitet. Deswegen mein Gedanke, dass ich hier socket.io verwenden muss.
Oder geht das auch ohne socket.io? Vermutlich nicht, wenn es unterscheidliche Dinge sind. Nur stellt sich dann die Frage, warum so ein wichtiges Detail nicht direkt im Einführungstext geschrieben wird, anstatt nur nebenbei in einem beispiel...
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Mag mir wirklich keiner helfen? Bin ich mit dem Modul auf dem richtigen Weg?

Wenn ich das "verify=false" weglasse, kommt keine Warnung mehr, aber immernoch derselbe StopIteration Fehler...
BlackJack

@Serpens66: Ich würde mal das Logging für `requests` aktivieren wie auf der PyPI-Seite für das socketIO-client-Modul beschrieben.

Steht auf der Bitcoin-Seite welche Version des socket.io-Protokolls verwendet wird?
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

BlackJack hat geschrieben:@Serpens66: Ich würde mal das Logging für `requests` aktivieren wie auf der PyPI-Seite für das socketIO-client-Modul beschrieben.

Steht auf der Bitcoin-Seite welche Version des socket.io-Protokolls verwendet wird?
Danke für deine Antwort :)

Meinst du ich soll zufügen:

Code: Alles auswählen

#For debugging information, run these commands first.

import logging
logging.getLogger('requests').setLevel(logging.WARNING)
logging.basicConfig(level=logging.DEBUG)
Nein, die Version steht leider nirgends. =/ müsste man wohl zur Not durch ausprobieren rauskriegen.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Ich habe selbst ein Modul gefunden, welches wohl am Besten geeignet ist. Und zwar websockets:
https://websockets.readthedocs.org/en/stable/index.html

Ich werde damit mal ein wenig rumprobieren. Habe schon erste Erfolge gehabt, aber so ganz vollständig ist die Doku dort leider auch nicht, deswegen muss ich noch ein wenig experimentieren...
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Soo... ich habe jetzt 2 skripte die 2 unterschiedliche websocket module verwenden.
Aber bei beiden stehe ich vor demselben Problem. Nämlich, dass ich noch nicht raus habe, wie die websocket Verbindung nun im hintergrund weiterläuft.

Code: Alles auswählen

import time
import json
import requests
import websocket

class btc_ws:
    
    def __init__(self):
        print("btc_ws gestartet")
        self.orderbook = []
        
        r = requests.request("GET",'https://ws.bitcoin.de/socket.io/1/?t='+str(time.time()).split('.')[0],timeout=20)
        self.hskey = r.text.split(':')[0]
        self.time = str(time.time() * 1000).split('.')[0]
        
        host = "wss://ws.bitcoin.de/socket.io/1/websocket/"+str(self.hskey)
        websocket.enableTrace(False)
        
        self.websocket = websocket.WebSocketApp(host,
                                    on_message = self.on_message,
                                    on_error = self.on_error,
                                    on_close = self.on_close)
        self.websocket.on_open = self.on_open
        
        self.websocket.run_forever()  
        print("init zuende")
    
    def on_message(self,ws,evt=0):
        if evt:
            evt = evt.replace("€","EUR") 
            print("Event: {} , ws: {}".format(evt,ws))

    def on_open(self,ws):
        print("open")
        
    def on_error(self,ws=0,evt=0):
        print("Error: {} , ws: {}".format(evt,ws))

    def on_close(self,ws):
        print('DISCONNECT, ws: {}'.format(ws))
    
    def give_orderbook(self):
        print("give_orderbook")
        return(self.orderbook,self.time)
    
    def close_all(self):
        print ('closeall')
        self.websocket.close()
        
if __name__ == '__main__':
    btc = btc_ws()
    # print(btc.give_orderbook())
    # sleep(5)
    # print(btc.give_orderbook())
    # btc.close_all()  

Das ist die erste Variante mit "websocket". Funktioniert denke ich auch ganz gut.
Auch die zweite Variante funktioniert, wobei ich mir noch unsicher bin, wieso asyncio in der 2ten Variante verwendet wird. Aber auch gut möglich, dass ich es schlicht falsch verwende :D

Code: Alles auswählen

import asyncio
import websockets
import requests
import time

class btc_ws2:
    
    def __init__(self):
        print("btc_ws2 gestartet")
        self.orderbook = []

        r = requests.request("GET",'https://ws.bitcoin.de/socket.io/1/?t='+str(time.time()).split('.')[0],timeout=20)
        self.hskey = r.text.split(':')[0]
        self.time = str(time.time() * 1000).split('.')[0]

        asyncio.get_event_loop().run_until_complete(self.handler())
        # asyncio.get_event_loop().close() # ?
        print("init zuende")
    
    def give_orderbook(self):
        print("give_orderbook")
        return(self.orderbook,self.time)
    
    def close_all(self):
        self.websocket.close()
        asyncio.get_event_loop().stop()
        asyncio.get_event_loop().close()
        
    @asyncio.coroutine
    def consumer(self,message):
        self.time = str(time.time() * 1000).split('.')[0]
        self.orderbook.append(message)
        print(message)
            
    @asyncio.coroutine
    def handler(self):
        self.websocket = yield from websockets.connect('wss://ws.bitcoin.de/socket.io/1/websocket/'+str(self.hskey))
        try:
            while True:
                message = yield from self.websocket.recv()
                message = message.replace("€","EUR")
                yield from self.consumer(message)
        finally:
            yield from self.close_all()
        
if __name__ == '__main__':
    btc = btc_ws2()
    print(btc.give_orderbook())
    sleep(5)
    print(btc.give_orderbook())
    btc.close_all()
Ja. Also der "if __name__ == '__main__':" Kram simuliert quasi das Aufrufen des Skripts von außen. Ich möchte die websocket Verbindung halt starten und aufrecht erhalten. Und dann von außen dann und wann eben das aktuelle Orderbook abrufen können.

Das Problem bei beiden Skripten ist allerdings, dass es schon im _init_ festhängt, da wir dort ja festlegen, dass die websocket quasi für immer laufen soll. Und _init_ ist damit erst beendet, wenn die verbindung beendet ist :D
Also mache ich offensichtlich noch irgendwas falsch.. denn eine websocket ist doch eig dafür gemacht eben im Hintergrund zu laufen und nicht den ganzen Betrieb aufzuhalten ^^

edit:
Python code tags funktionieren scheinbar immernoch nicht? =/
[Codebox=python file=Unbenannt.py]
def test():
print("hallo")
[/Codebox]
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Serpens66 hat geschrieben:Also mache ich offensichtlich noch irgendwas falsch.. denn eine websocket ist doch eig dafür gemacht eben im Hintergrund zu laufen und nicht den ganzen Betrieb aufzuhalten ^^
Nein, dafür sind Websockets nicht gedacht. Du musst dich schon selbst darum kümmern.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

DasIch hat geschrieben:
Serpens66 hat geschrieben:Also mache ich offensichtlich noch irgendwas falsch.. denn eine websocket ist doch eig dafür gemacht eben im Hintergrund zu laufen und nicht den ganzen Betrieb aufzuhalten ^^
Nein, dafür sind Websockets nicht gedacht. Du musst dich schon selbst darum kümmern.
okay, und wie mache ich das?
asyncio ist ja, soweit ich es verstanden habe, schon dafür gedacht, dass mehrere Dinge gleichzeitg gemacht werden können.
Aber wie ich das so implementiere, dass die gewünschte Funktionsweise bei herauskommt, kann ich mir rein logisch schon nicht vorstellen.

Wäre also für Hilfe dankbar :)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Serpens66 hat geschrieben:okay, und wie mache ich das?
Es gibt jede Menge Informationen zu asyncio im speziellen und asynchroner Programmierung im Allgemeinen. Die kann man nicht brauchbar in ein paar Zeilen zu vermitteln. Du musst dich schon selbst mit solchen grundlegenden Themen auseinandersetzen.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

DasIch hat geschrieben:
Serpens66 hat geschrieben:okay, und wie mache ich das?
Es gibt jede Menge Informationen zu asyncio im speziellen und asynchroner Programmierung im Allgemeinen. Die kann man nicht brauchbar in ein paar Zeilen zu vermitteln. Du musst dich schon selbst mit solchen grundlegenden Themen auseinandersetzen.
das habe ich bereits, sonst wäre obiges skript nicht entstanden.
Ich weiß wie ich einen parallelen Durchlauf mit asyncio starte und manage.
Aber ich weiß nicht, wie das zu der gewollten funktionsweise führen kann.
Also wäre ich dir dankbar, wenn du mir den logischen schritt aufzeigen könntest und evlt auch ein spezielleres Stichwort zu asyncio geben kannst, welche Funktion davon zu dem gewollten Ergebnis führt.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Serpens66 hat geschrieben:das habe ich bereits, sonst wäre obiges skript nicht entstanden.
Nein, hast du nicht, du glaubst es nur.
Ich weiß wie ich einen parallelen Durchlauf mit asyncio starte und manage.
Auch das weißt du nicht. Kannst du nicht wissen weil bei asyncio nichts parallel passiert. Was du wüsstest, hättest du dich mit den Grundlagen auseinandergesetzt...

Du musst die gesamte Anwendung so strukturieren dass sie im Rahmen der Event Loop ausgeführt wird, so wie der websocket Teil auch.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

DasIch hat geschrieben: Du musst die gesamte Anwendung so strukturieren dass sie im Rahmen der Event Loop ausgeführt wird, so wie der websocket Teil auch.
okay, danke für den tipp.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Kann mir jemand vllt noch erklären, wieso in manchen websocket Modulen threading verwendet wird, sie aber dennoch nicht so wie gewollt im Hintergrund laufen?

Hier der erste Code funktioniert ja auch ohne threading und Co: viewtopic.php?f=1&t=38120&p=292409#p292401

Bin jetzt noch auf ein weiteres Modul gestoßen, in dem threading verwendet wird:
https://github.com/Lawouach/WebSocket-f ... dclient.py
Und hatte gehofft, dass ich dadurch eben nicht folgendes tun muss:
Serpens66 hat geschrieben:Du musst die gesamte Anwendung so strukturieren dass sie im Rahmen der Event Loop ausgeführt wird, so wie der websocket Teil auch.
Aber scheinbar war das nichts, denn im folgendem Code läuft es weiterhin nicht im hintergrund:

Code: Alles auswählen

from ws4py.client.threadedclient import WebSocketClient
import time
import json
import requests


class bitcoinde_ws3():
    def __init__(self):
        print("bitcoinde_ws3 init aufgerufen")
    
    def starte(self,platzhalter=0,platzhalter2=0):
        print("bitcoinde_ws3 gestartet")
        r = requests.request("GET",'https://ws.bitcoin.de/socket.io/1/?t='+str(time.time()).split('.')[0],timeout=20)
        hskey = r.text.split(':')[0]

        url = "wss://ws.bitcoin.de/socket.io/1/websocket/"+str(hskey)
        try:
            self.ws = DummyClient(url)
            self.ws.connect()
            self.ws.run_forever()
        except KeyboardInterrupt:
            self.ws.close()
        print("bitcoinde_ws3 start zuende")

class DummyClient(WebSocketClient):
    
    def opened(self):
        pass 

    def closed(self, code, reason=None):
        print("Closed down", code, reason)

    def received_message(self, m):
        if len(m) == 175:
            print(m)
            self.close(reason='Bye bye')
        m = str(m)
        if "2::" in m:
            self.send('2::') 
        elif m:
            m = m.replace("€","EUR") ## alle € zeichen in EUR umwandeln, damit es zb beim printen keine fehlermeldung gibt.
            print("{} | {}".format(time.asctime(),m))

if __name__ == '__main__':
    btc_ws3 = bitcoinde_ws3()
    btc_ws3.starte()
    print(btc_ws3.give_orderbook())
    time.sleep(5)
    print(btc_ws3.give_orderbook())
    time.sleep(5)
    print(btc_ws3.give_orderbook())
Ich frage mich nun also, warum in dem code für ws4py.client.threadedclient überhaupt treading genutzt wird, wenn es doch auch ohne das genauso gut/schlecht zu klappen scheint.

Ich habe zwar einen Weg gefunden, wie ich alles parallel laufen lasse, sodass es so funktioniert wie ich möchte, aber ich hatte gehofft doch noch ein Modul zu finden, wo es direkt im hitergrund läuft. Denn zb. beim pusherclient ist es so, dass ich nichts mit threads oderso machen muss, der pusherclient läuft automatisch im hintergrund:
https://github.com/ekulyk/PythonPusherC ... nection.py
BlackJack

@Serpens66: Was hattest Du denn jetzt von einer Methode erwartet die `run_forever()` heisst? Ruf die einfach nicht auf und schon kannst Du stattdessen etwas anderes machen. Aber Vorsicht: Dann *musst* Du auch etwas machen, denn wen Du das nicht tust und das Programm deswegen endet, dann endet auch der Websocket-Client-Thread.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

BlackJack hat geschrieben:@Serpens66: Was hattest Du denn jetzt von einer Methode erwartet die `run_forever()` heisst? Ruf die einfach nicht auf und schon kannst Du stattdessen etwas anderes machen. Aber Vorsicht: Dann *musst* Du auch etwas machen, denn wen Du das nicht tust und das Programm deswegen endet, dann endet auch der Websocket-Client-Thread.
Vielen Dank für diesen Hinweis!!! :)
Habe es auch schon auskommentiert und es funktioniert :D So lang mein Hauptskript, welches die websocket aufruft, in einer endlosschließe läuft, läuft also auch die websocket verbindung. Sehr gut! :)

edit:
bleibt nur noch die Frage nach dem threading im Client.
Was meint ihr denn zu meinem ersten Code hier:
viewtopic.php?f=1&t=38120#p292401
Kann ich den problemlos verwenden (muss noch "if "2::" in m: self.send('2::')" zufügen), oder sollte ich lieber ein Modul verwenden, also zb. das hier:
viewtopic.php?f=1&t=38120#p292684
Antworten