Quellcode durchsuchen

Websocket Client hinzugefügt

Kolja Strohm vor 5 Jahren
Ursprung
Commit
2694c1a4a2
4 geänderte Dateien mit 417 neuen und 0 gelöschten Zeilen
  1. 2 0
      Network/Network.vcxproj
  2. 6 0
      Network/Network.vcxproj.filters
  3. 342 0
      Network/WebSocket.cpp
  4. 67 0
      Network/WebSocket.h

+ 2 - 0
Network/Network.vcxproj

@@ -181,12 +181,14 @@ copy "x64\Release\Network.dll" "..\..\..\Spiele Platform\SMP\Fertig\x64\network.
     <ClInclude Include="Klient.h" />
     <ClInclude Include="Network.h" />
     <ClInclude Include="Server.h" />
+    <ClInclude Include="WebSocket.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="HttpRequest.cpp" />
     <ClCompile Include="Klient.cpp" />
     <ClCompile Include="Network.cpp" />
     <ClCompile Include="Server.cpp" />
+    <ClCompile Include="WebSocket.cpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 6 - 0
Network/Network.vcxproj.filters

@@ -27,6 +27,9 @@
     <ClInclude Include="HttpRequest.h">
       <Filter>Headerdateien</Filter>
     </ClInclude>
+    <ClInclude Include="WebSocket.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">
@@ -41,5 +44,8 @@
     <ClCompile Include="HttpRequest.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="WebSocket.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 342 - 0
Network/WebSocket.cpp

@@ -0,0 +1,342 @@
+#include "WebSocket.h"
+#include <Random.h>
+#include "HttpRequest.h"
+#include <iostream>
+
+using namespace Network;
+using namespace WebSocket;
+using namespace Framework;
+
+__declspec( dllexport ) Frame &Frame::operator+=( const Frame &b ) // baut frames zusammen welche zu einer nachricht gehören
+{
+    fin = b.fin;
+    if( opcode == 0 )
+        opcode = b.opcode;
+    dataLength += b.dataLength;
+    char *data = new char[ dataLength ];
+    memcpy( data, this->data, dataLength - b.dataLength );
+    memcpy( data + dataLength, b.data, b.dataLength );
+    delete[] this->data;
+    this->data = data;
+    return *this;
+}
+
+
+__declspec( dllexport ) WebSocketClient::WebSocketClient( const char *path, const char *host, unsigned short port )
+    : Thread()
+{
+    queue = new Array< Frame >();
+    callback = 0;
+    klient = 0;
+    this->path = path;
+    this->host = host;
+    this->port = port;
+    lastPingFrame = 0;
+}
+
+WebSocketClient::~WebSocketClient()
+{
+    disconnect();
+    while( queue->getEintragAnzahl() )
+    {
+        Frame f = queue->get( 0 );
+        delete[] f.data;
+        queue->remove( 0 );
+    }
+    queue->release();
+}
+
+__declspec( dllexport ) void WebSocketClient::setMessageCallback( std::function< void( WebSocketClient *, __int64 size, const char *data, DataType typ ) > callback )
+{
+    this->callback = callback;
+}
+
+__declspec( dllexport ) bool WebSocketClient::connect()
+{
+    char allowedKeyChars[] = { 'a', 'b','c','d','e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+        '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+        'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0 };
+    __int64 numKeyChars = strlen( allowedKeyChars );
+    RandomGenerator gen;
+    if( !klient )
+    {
+        Text message = "GET ";
+        message += path;
+        message += " HTTP/1.1\n";
+        message += "Host: ";
+        message += host;
+        message += "\nUpgrade: websocket\n";
+        message += "Connection: Upgrade\n";
+        char *key = new char[ 25 ];
+        key[ 24 ] = 0;
+        key[ 23 ] = '=';
+        key[ 22 ] = '=';
+        for( int i = 0; i < 22; i++ )
+            key[ i ] = allowedKeyChars[ (int)( gen.rand() * numKeyChars ) ];
+        message += "Sec-WebSocket-Key: ";
+        message += key;
+        delete[] key;
+        message += "\nSec-WebSocket-Version: 13\n\n";
+        klient = new Klient();
+        if( !klient->verbinde( port, host ) )
+        {
+            klient = klient->release();
+            return 0;
+        }
+        klient->sende( message, message.getLength() );
+        Text answer;
+        int br = 0;
+        do
+        {
+            char buff[ 2 ];
+            buff[ 1 ] = 0;
+            if( klient->getNachricht( buff, 1 ) )
+                answer += buff;
+            else
+                break;
+            if( buff[ 0 ] == '\n' )
+                br++;
+            else if( buff[ 0 ] != '\r' )
+                br = 0;
+        } while( br < 2 && klient->hatNachricht( 1000 ) );
+        HTTP::Answer *handshakeResponse = new HTTP::Answer( answer );
+        if( handshakeResponse->getStatusCode() != 101 )
+        {
+            handshakeResponse->release();
+            klient = klient->release();
+            return 0;
+        }
+        handshakeResponse->release();
+        start();
+        return 1;
+    }
+    else
+        return 1;
+}
+
+__declspec( dllexport ) bool WebSocketClient::send( __int64 size, const char *data, DataType typ )
+{
+    Frame f;
+    f.fin = 1;
+    f.rsv1 = 0;
+    f.rsv2 = 0;
+    f.rsv3 = 0;
+    f.mask = 1;
+    if( typ == TEXT )
+        f.opcode = 1;
+    else
+        f.opcode = 2;
+    f.dataLength = size;
+    f.data = new char[ f.dataLength ];
+    memcpy( f.data, data, f.dataLength );
+    f.key[ 0 ] = 1;
+    f.key[ 1 ] = 2;
+    f.key[ 2 ] = 3;
+    f.key[ 3 ] = 4;
+    c.lock();
+    queue->add( f );
+    c.unlock();
+    return 1;
+}
+
+__declspec( dllexport ) void WebSocketClient::thread()
+{
+    while( klient )
+    {
+        c2.lock();
+        if( !klient )
+        {
+            c2.unlock();
+            return;
+        }
+        if( klient->hatNachricht( 100 ) )
+        {
+            c2.unlock();
+            bool first = 1;
+            Frame m;
+            unsigned char byte;
+            do
+            {
+                c2.lock();
+                if( !klient )
+                {
+                    c2.unlock();
+                    return;
+                }
+                Frame message;
+                klient->getNachricht( (char*)&byte, 1 );
+                message.fin = ( byte & 0x80 ) != 0;
+                message.rsv1 = ( byte & 0x40 ) != 0;
+                message.rsv2 = ( byte & 0x20 ) != 0;
+                message.rsv3 = ( byte & 0x10 ) != 0;
+                message.opcode = byte & 0xF;
+                klient->getNachricht( (char*)&byte, 1 );
+                message.mask = ( byte & 0x80 ) != 0;
+                message.dataLength = byte & 0x7F;
+                if( message.dataLength == 126 )
+                {
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength = byte << 8;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= byte;
+                }
+                else if( message.dataLength == 127 )
+                {
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength = (__int64)byte << 56;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 48;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 40;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 32;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 24;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 16;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte << 8;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.dataLength |= (__int64)byte;
+                }
+                if( message.mask )
+                {
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.key[ 0 ] = byte;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.key[ 1 ] = byte;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.key[ 2 ] = byte;
+                    klient->getNachricht( (char*)&byte, 1 );
+                    message.key[ 3 ] = byte;
+                }
+                message.data = 0;
+                if( message.dataLength )
+                    message.data = new char[ message.dataLength ];
+                for( int i = 0; i < message.dataLength; i++ )
+                {
+                    klient->getNachricht( (char*)&byte, 1 );
+                    if( message.mask )
+                        message.data[ i ] = byte ^ message.key[ i % 4 ];
+                    else
+                        message.data[ i ] = byte;
+                }
+                c2.unlock();
+                if( first )
+                    m = message;
+                else
+                {
+                    m += message;
+                    delete[] message.data;
+                }
+                first = 0;
+            } while( !m.fin );
+            if( callback )
+                callback( this, m.dataLength, m.data, m.opcode == 1 ? TEXT : BINARY );
+            delete[] m.data;
+        }
+        else
+        {
+            c2.unlock();
+            c.lock();
+            while( queue->getEintragAnzahl() )
+            {
+                Frame f = queue->get( 0 );
+                c.unlock();
+                unsigned char byte = ( f.fin ? 1 : 0 ) << 7;
+                byte |= ( f.rsv1 ? 1 : 0 ) << 6;
+                byte |= ( f.rsv2 ? 1 : 0 ) << 5;
+                byte |= ( f.rsv3 ? 1 : 0 ) << 4;
+                byte |= f.opcode & 0xF;
+                c2.lock();
+                if( !klient )
+                {
+                    c2.unlock();
+                    return;
+                }
+                klient->sende( (char*)&byte, 1 );
+                byte = ( f.mask ? 1 : 0 ) << 7;
+                if( f.dataLength < 126 )
+                    byte |= (unsigned char)f.dataLength & 0x7F;
+                else if( f.dataLength <= 32767 )
+                    byte |= 126;
+                else
+                    byte |= 127;
+                klient->sende( (char*)&byte, 1 );
+                if( f.dataLength >= 126 )
+                {
+                    if( f.dataLength <= 32767 )
+                    {
+                        byte = (unsigned char)( f.dataLength >> 8 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)f.dataLength & 0xFF;
+                        klient->sende( (char*)&byte, 1 );
+                    }
+                    else
+                    {
+                        byte = (unsigned char)( f.dataLength >> 56 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 48 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 40 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 32 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 24 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 16 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)( f.dataLength >> 8 );
+                        klient->sende( (char*)&byte, 1 );
+                        byte = (unsigned char)f.dataLength & 0xFF;
+                        klient->sende( (char*)&byte, 1 );
+                    }
+                }
+                if( f.mask )
+                {
+                    byte = (unsigned char)f.key[ 0 ];
+                    klient->sende( (char*)&byte, 1 );
+                    byte = (unsigned char)f.key[ 1 ];
+                    klient->sende( (char*)&byte, 1 );
+                    byte = (unsigned char)f.key[ 2 ];
+                    klient->sende( (char*)&byte, 1 );
+                    byte = (unsigned char)f.key[ 3 ];
+                    klient->sende( (char*)&byte, 1 );
+                }
+                if( f.dataLength )
+                {
+                    for( int i = 0; i < f.dataLength; i++ )
+                    {
+                        if( f.mask )
+                            byte = (unsigned char)f.data[ i ] ^ f.key[ i % 4 ];
+                        else
+                            byte = (unsigned char)f.data[ i ];
+                        klient->sende( (char*)&byte, 1 );
+                    }
+                }
+                c2.unlock();
+                delete[] f.data;
+                c.lock();
+                queue->remove( 0 );
+            }
+            c.unlock();
+        }
+    }
+}
+
+__declspec( dllexport ) void WebSocketClient::disconnect()
+{
+    if( !klient )
+        return;
+    c2.lock();
+    klient->trenne();
+    klient = klient->release();
+    c2.unlock();
+    warteAufThread( 1000 );
+}
+
+__declspec( dllexport ) bool WebSocketClient::isConnected() const
+{
+    return klient != 0;
+}

+ 67 - 0
Network/WebSocket.h

@@ -0,0 +1,67 @@
+#pragma once
+
+#include <functional>
+#include <Thread.h>
+#include <Critical.h>
+#include "Klient.h"
+
+namespace Network
+{
+    namespace WebSocket
+    {
+        struct Frame
+        {
+            bool fin, rsv1, rsv2, rsv3; // bit 0 bis 3
+            char opcode; // bit 4 bis 7
+            bool mask; // bit 8
+            __int64 dataLength; // bit 9 bis 15 oder bis 31 oder bis 79
+            unsigned char key[ 4 ]; // wenn mask = 1 dann die nächsten 32 bits 
+            char *data; // die daten
+             // baut frames zusammen welche zu einer nachricht gehören
+            __declspec( dllexport ) Frame &operator+=( const Frame &b );
+        };
+
+        enum DataType
+        {
+            TEXT,
+            BINARY
+        };
+
+        class WebSocketClient : public Framework::Thread
+        {
+        private:
+            std::function< void( WebSocketClient *, __int64 size, const char *data, DataType typ ) > callback;
+            Klient *klient;
+            Framework::Text path;
+            Framework::Text host;
+            Frame *lastPingFrame;
+            Array< Frame > *queue;
+            unsigned short port;
+            Critical c;
+            Critical c2;
+
+        public:
+            // Erstellt ein neues Websocket
+            //  path: Der Pfad zur Resource
+            //  host: Die Adresse des Servers
+            //  port: Der Port des Servers
+            __declspec( dllexport ) WebSocketClient( const char *path, const char *host, unsigned short port );
+            __declspec( dllexport ) virtual ~WebSocketClient();
+            // Setzt die Funktion, welche zum verarbeiten von Servernachrichten aufgerufen werden soll
+            __declspec( dllexport ) void setMessageCallback( std::function< void( WebSocketClient *, __int64 size, const char *data, DataType typ ) > callback );
+            // Führt den WebSocket Handshake aus falls noch nicht verbunden
+            __declspec( dllexport ) bool connect();
+            // Sendet eine Nachricht an den Server
+            //  size: Die länge der Nachricht
+            //  data: Die Daten
+            //  typ: Den Typ der Nachricht
+            __declspec( dllexport ) bool send( __int64 size, const char *data, DataType typ = TEXT );
+            // Diese Funktion verarbeitet Servernachrichten. Sie sollte nicht manuell aufgerufen werden, da sie automatisch gestartet wird
+            __declspec( dllexport ) void thread() override;
+            // bricht die verbindung ab
+            __declspec( dllexport ) void disconnect();
+            // Gibt 1 zurück, wenn eine Verbindung zum Server besteht, 0 sonnst
+            __declspec( dllexport ) bool isConnected() const;
+        };
+    }
+}