AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi TServerSocket und Benutzeranmeldung
Thema durchsuchen
Ansicht
Themen-Optionen

TServerSocket und Benutzeranmeldung

Ein Thema von Schwedenbitter · begonnen am 25. Nov 2009 · letzter Beitrag vom 27. Nov 2009
Antwort Antwort
Schwedenbitter

Registriert seit: 22. Mär 2003
Ort: Finsterwalde
622 Beiträge
 
Turbo Delphi für Win32
 
#1

TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 12:16
Hallo,

ich habe TurboDelphi und bin daher in der Benutzung fremder Komponenten eingeschränkt. Ich habe bereits mittels entsprechender Tutorials (z.B. hier) ein Chat-Programm ausschließlich mit TServerSocket und TClientSocket nachgebastelt. Allerdings hat das keine Authentifizierung der Benutzer und bringt nur Broadcast.
Letzteres ist kein Problem (mehr), weil die Clients gleich als erstes die Benutzernamen an den Server senden und ich damit eine Liste parallel zu Socket.Connections[I] verwalte. Ich weiß also, welcher Benutzer welchen Index I hat. Die Textnachrichten enthalten dann immer einen selbstgebauten "Header", der Absender, Empfänger, Zeit etc. enthält.

Meine Idee ist es nun, bereits im Ereignis TServerSocket.OnClientConnect die Prüfung von Benutzernamen und Passwort mit reinzubasteln. Wenns nicht klappt, soll die Verbindung gleich wieder gekappt werden.

Nun meine Fragen:
  • Kann man das generell so machen?
  • Wenn ja, wie?
  • Gibt es andere Alternativen (unter Beachtung meiner Probleme mit Fremdkomponenten bei TurboDelphi)?
Gruß, Alex
Alex Winzer
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#2

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 13:18
In OnclientConnect geht sowas gar nicht. Sondern du musst das Passwort anfordern, und es dann überprüfen. Dazu musst du dir ein entsprechendes Protokoll überlegen.
Bisher besteht dein Protokoll nur darin, dass der zuerst gesendete String immer der Name ist. Alles andere sind Nachrichten.

Jetzt musst du dir halt ein bisschen mehr überlegen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Schwedenbitter

Registriert seit: 22. Mär 2003
Ort: Finsterwalde
622 Beiträge
 
Turbo Delphi für Win32
 
#3

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 14:10
Zitat von sirius:
... Dazu musst du dir ein entsprechendes Protokoll überlegen.
Danke für die klare Antwort. Einen Plan habe ich schon. Mein Ansinnen war es aber, das mit dem Aushandeln verbundene Ping-Pong zu verhindern. Außerdem habe ich dann ein Problem, falls sich jemand einhacken will und meinen armen Server dauernd mit entsprechenden Kombinationen bombardiert.

Bevor ich nun einen Haufen Zeit investiere:
Ich habe schon damit angefangen, dem jeweiligen String als erstes Zeichen einen Integer-Wert mitzugeben. Vom Server kommend bedeutet z.B. eine Eins: "Schicke mir erstmal Deinen Benutzernamen ." Und vom Client kommend bedeutet es: "Hier hast Du ihn!" ...

SQL-Code:
Client -> Server (Verbindung)
Server -> Client #01
Client -> Server #01 + 'Benutzername'
Server (Prüfung ob OK, falls nicht sofort trennen)
Server -> Client #02
Client -> Server #02 + 'Passwort'
Server (Prüfung ob OK, falls nicht sofort trennen)
Kann ich das so weiter führen oder gibt es dagegen irgend welche Bedenken?
Gibt es bessere Ideen?


Gruß, Alex
Alex Winzer
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#4

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 14:23
obligatorischer Hinweis Nummer 1:
TCP sichert nicht ab, dass ein Paket zusammen ankommt, es kann auch zerstückelt werden, oder gleich zwei hintereinander gesendete kommen als eins an. Du brauchst also entweder ein Ende-Zeichen je Nachricht, oder du hast ein Protokoll-Header, welcher an definierter Stelle, die Größe des Gesamtpaketes mitsendet. Natürlich musst du hinterher die Pakete wieder trennen. (zum Testen musst du mal Benutzernmane über IMHO 1000 Bytes versenden.)
Ansonsten sieht dein Protokoll soweit ok aus.
Ich würde aber bei falscher Benutzereingabe nicht gleich trennen, sondern erst eine Meldung schicken und dann trennen.

Gegen DOS o.ä. -Attacken kannst du dich vielleicht auch wehren. Hier schaust du dir in OnConnect mal die RemotAddresse des Sockets an.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Schwedenbitter

Registriert seit: 22. Mär 2003
Ort: Finsterwalde
622 Beiträge
 
Turbo Delphi für Win32
 
#5

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 14:58
Nochmals vielen Dank!

Zitat von sirius:
... oder du hast ein Protokoll-Header, welcher an definierter Stelle, die Größe des Gesamtpaketes mitsendet...
Ich meine mich aus Anlass meiner vorherigen Suche daran zu erinnern, dass hier mal irgendwo mal so ein Header vorgschlagen wurde. Weiß jemand den Link und kann ihn mir schicken, bevor ich wieder rumwühlen muss Natürlich suche ich sonst heute Abend selber.

Zitat von sirius:
Gegen DOS o.ä. -Attacken kannst du dich vielleicht auch wehren. Hier schaust du dir in OnConnect mal die RemotAddresse des Sockets an.
Also rein Praktisch?
Speichere ich in einem Zähler, wie oft es von einer bestimmten IP Anmeldeversuche gegeben hat. Und wenn es zuviel sind, trenne ich immer gleich. Oder wie?
Dann sehe ich aber das Problem, dass ja später mal ein lieber Benutzer mit derselben IP kommen kann. Der käme dann nicht mehr rein.

Gruß, Alex
Alex Winzer
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#6

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 15:29
Zitat von Schwedenbitter:
Ich meine mich aus Anlass meiner vorherigen Suche daran zu erinnern, dass hier mal irgendwo mal so ein Header vorgschlagen wurde.
Hier gibts einige. Und dann gibt es ja noch die bekannten PaketHeader von HTTP, FTP, ... bei denen du dir was abschauen kannst.
ICh weis ja nicht, wo du noch hin willst, aber reicht es für dein obiges Beispiel nicht auch erstmal am Ende ein #0 zu schicken. Da weist du hinterher zumindest, wann Schluss ist (bei http wird auch nur eine Leerzeile am Ende gesendet).


Zitat:
Also rein Praktisch?
Speichere ich in einem Zähler, wie oft es von einer bestimmten IP Anmeldeversuche gegeben hat. Und wenn es zuviel sind, trenne ich immer gleich. Oder wie?
Wäre mein erster Ansatz. Habe mich aber auch noch nicht weiter damit befasst.

Zitat:
Dann sehe ich aber das Problem, dass ja später mal ein lieber Benutzer mit derselben IP kommen kann. Der käme dann nicht mehr rein.
Irgendwann (also nach 60 Minuten oder so) würde ich die IP dann wieder freigeben. Evtl. auch den Port dazu merken.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
uoeb7gp
(Gast)

n/a Beiträge
 
#7

Re: TServerSocket und Benutzeranmeldung

  Alt 25. Nov 2009, 23:23
Hallo Schwedenbitter, eine Serverauthentifizierung kannst Du folgendermaßen implementieren.

1. Du definierst Dir einen Protokollheader, dieser Header ist logisch von den Daten getrennt.

Delphi-Quellcode:
{GENERIC HEADER}
  //-- Headeraufbau MsgID = Byte = 0..255; Msg-Length = DWORD
  //-- +-----------------------------------------------|----------------+
  //-- | Transport Header | Daten Header |
  //-- +--------------------|--------------------------|----------------+
  //-- | Byte-MsgID | 4Byte-DWORD Msg-Length | Daten |
  //-- +--------------------|--------------------------|----------------+

  MSG_ID_LOGON : Byte = 0;
  MSG_ID_DATA : Byte = 1;
  MSG_ID_KEEP_ALLIVE : Byte = 2;

{MSG_ID_LOGON}
  //-- Aufbau der Datenstruktur [Daten] für MSG_ID_LOGON
  //-- +--------------------+
  //-- | TLogonRec |
  //-- +--------------------+
Datenstruktur [Daten Header]

Delphi-Quellcode:
type
  TLogonSVRRec = packed record
    NTUser : array[0..31] of Char;
    Passwort : array[0..31] of Char;
    RechnerName : array[0..31] of Char;
    RechnerIP : array[0..31] of Char;
    GUID : array[0..63] of Char;
    UserFullName : array[0..128] of Char;
    AnyIntData1 : integer;
    AnyIntData2 : integer;
    ptrClientObj : pointer; //-- out
  end;
  PLogonSVRRec = ^TLogonSVRRec;


  TDataRec = packed record
    Socket : integer;
    ptrData : Pointer;
    cbSize : DWORD;
    Error : integer;
    ErrorMsg : PChar;
    ptrLogonSVRRec : PLogonSVRRec;
  end;
  PDataRec = ^TDataRec;
2. Im ClientSocket Connect sendest du die Daten an den Server!

Delphi-Quellcode:
procedure TClientObj.ClientSocketConnect(const Sender: TObject;
  Socket: TCustomWinSocket);
begin
  try
    FSocketState := stConnect;
    if Assigned(CBConnect) then begin
       CBConnect('Client-Socket Connected !');
    end;
    if (ClientObj <> NIL) then
       SendMsg(MSG_ID_LOGON, @MainClass.LogonInfo,
         SizeOf(TLogonSVRRec), ClientSocket.Socket);
  except
    FSocketState:=stError;
  end;
end;
3. Die Procedure SendMsg generiert den 5 Byte Header und fügt die Daten hinzu.

Delphi-Quellcode:
procedure TClientObj.SendMsg(MsgID: Byte; ptrData: Pointer; cbSize: Integer;
  Socket: TCustomWinSocket);
const
  HEADER_SIZE = sizeof(Byte) + sizeof(integer);
var
  ErrorCode:integer;
  fdSet: TFDSet;
Begin
  try
    FSendBuf.Seek(0, 0);
    FSendBuf.Write(MsgID, sizeof(Byte));
    FSendBuf.Write(cbSize, sizeof(integer));
    FSendBuf.Write(ptrData^, cbSize);
    Inc(cbSize, HEADER_SIZE);

    if socket.SendBuf(FSendBuf.memory^, cbSize, ErrorCode) <= 0 then begin
      if ErrorCode = WSAEWOULDBLOCK then begin
        FD_ZERO(fdSet);
        fdSet.fd_count := 1;
        fdSet.fd_array[0] := socket.SocketHandle;

        if (Select(0, nil, @fdSet, nil, nil) <> 1) or
          (socket = NIL) or (not socket.Connected) then EXIT;

        socket.SendBuf(FSendBuf.memory^, cbSize, ErrorCode);
      end;
    end;
    
  except
    FSocketState := stError;
  end;
end;
4. Am server Extrahierst du den Header.

Delphi-Quellcode:
procedure TServerObj.ServerSocketClientConnect(const Sender: TObject;
  const Socket: TCustomWinSocket);
var
  DataRec: TDataRec;
begin
  try
    TCliCon.Create(FIntHash, FStringHash, Socket);
  except
    on e:exception do begin
      if Assigned(@CBClientError) then begin
        FillChar(DataRec, SizeOf(TDataRec), 0);
        DataRec.Socket := Socket.SocketHandle;
        DataRec.Error := wsagetlasterror;
        DataRec.ErrorMsg := PChar(
          'EXCEPTION > TServerObj.ServerSocketClientConnect: ' + e.Message
          );
        CBClientError(@DataRec);
      end;
    end;
  end;
end;

procedure TServerObj.ServerSocketClientRead(const Sender: TObject;
  const Socket: TCustomWinSocket);
var
  cbRcv: integer;
  RcvBuf: array [0..WSOCK_READ_BUFFER_SIZE * 8] of Byte;
  RcvBufItem: TRcvBufItem;
  hMemData: hGlobal;
  hMemStruct: hGlobal;
begin
  cbRcv := Socket.ReceiveBuf(RcvBuf[0], WSOCK_READ_BUFFER_SIZE * 8);
  if cbRcv <= 0 then EXIT;

  if Socket.SocketHandle <= 0 then EXIT;

  hMemData := GlobalAlloc(GMEM_FIXED, cbRcv);
  move(RcvBuf[0], Pointer(hMemData)^, cbRcv);

  hMemStruct := GlobalAlloc(GMEM_FIXED, SizeOf(TRcvBufItem));

  RcvBufItem.ptrData := Pointer(hMemData);
  RcvBufItem.cbData := cbRcv;
  RcvBufItem.Socket := Socket.SocketHandle;
  RcvBufItem.hMemStruct := hMemStruct;
  RcvBufItem.hMemData := hMemData;

  move(RcvBufItem, Pointer(hMemStruct)^, SizeOf(TRcvBufItem));

  if FRecvBuffer.Write(Pointer(hMemStruct), SizeOf(TRcvBufItem)) <>
    SizeOf(TRcvBufItem) then
  begin
    FRecvBuffer.SetBufSize((FRecvBuffer.BufSize + cbRcv) * 2);
    FRecvBuffer.Remove(SizeOf(TRcvBufItem));
    if FRecvBuffer.Write(Pointer(hMemStruct), SizeOf(TRcvBufItem)) <>
      SizeOf(TRcvBufItem) then
        raise exception.Create('you should never see this');
  end;

  SetEvent(FMsgDispatchEvent);
end;
5. Das erste Byte des Headers ist die MSG-ID, ist die Msg gültig, dann
solange aus dem Socket lesen, bis die 4Byte-DWORD Msg-Length erreicht ist.

Delphi-Quellcode:
procedure TServerObj.Execute;
const
  HEADER_LENGTH = SizeOf(byte) + SizeOf(DWORD);
var
  cbRcv: integer;
  cbWritten: integer;
  cbRest: integer;
  ptrByte: ^Byte;
  nLen: integer;
  nDataSize: integer;
  cb: integer;
  nMsgID: Byte;
  p: pointer;
  CliCon: TCliCon;
  RcvBuf: array [0..WSOCK_READ_BUFFER_SIZE * 8] of byte;
  RcvBufItem: TRcvBufItem;
begin
  while not terminated do begin

    if FRecvBuffer.CountBytes > 0 then begin

      RcvBufItem := TRcvBufItem(FRecvBuffer.Peek(nLen)^);

      if FRecvBuffer.Remove(SizeOf(TRcvBufItem)) <> SizeOf(TRcvBufItem) then
        raise exception.Create('nLen <> FRecvBuffer.Remove(nLen)');

      cbRcv := RcvBufItem.cbData;
      if cbRcv <= 0 then
        raise exception.create('cbRcv <= 0');

      move(RcvBufItem.ptrData^, RcvBuf[0], cbRcv);

      CliCon := FIntHash.ValueOf(RcvBufItem.Socket);
      if CliCon = nil then begin
        if GlobalFree(RcvBufItem.hMemData) <> 0 then
          raise exception.create('GlobalFree(RcvBufItem.hMemData)) <> 0');

        if GlobalFree(RcvBufItem.hMemStruct) <> 0 then
          raise exception.create('GlobalFree(RcvBufItem.hMemStruct) <> 0');
        Continue;
      end;

      cbWritten := CliCon.FMemBuf.Write(@RcvBuf[0], cbRcv);
      cbRest := cbRcv - cbWritten;
      if cbRest <> 0 then begin
        CliCon.FMemBuf.SetBufSize((CliCon.FMemBuf.BufSize + cbRcv) * 2);

        if CliCon.FMemBuf.Write(@RcvBuf[cbWritten], cbRest) <> cbRest then
          raise exception.Create('you should never see this');
      end;

      while true do begin
        nLen := HEADER_LENGTH;
        ptrByte := CliCon.FMemBuf.Peek(nLen);
        if ptrByte <> nil then
        begin
          move(ptrByte^, nMsgID, SizeOf(Byte));
          inc(ptrByte, SizeOf(Byte));
          move(ptrByte^, nDataSize, SizeOf(DWORD));
        end else begin
          Break;
        end;

        if nMsgID > MSG_ID_KEEP_ALLIVE then begin
          CliCon.FMemBuf.Remove(CliCon.FMemBuf.BufSize);
          raise exception.Create('not (nMsgID in [MSG_ID_LOGON..MSG_ID_KEEP_ALIVE])');
        end;

        if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH then
        begin //-- Mindestens ein Datensatz ist vorhanden
          if CliCon.FMemBuf.Remove(HEADER_LENGTH) = 0 then
            raise exception.Create('Buffer.Remove(HEADER_LENGTH) = 0');

          cb := nDataSize;
          p := CliCon.FMemBuf.Peek(cb);

          CliCon.ProcessMessage(nMsgID, p, nDataSize);

          if CliCon.FMemBuf.Remove(nDataSize) = 0 then
            raise exception.Create('Buffer.Remove(nDataSize) = 0');
        end else //-- if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH
          Break;
      end; //-- while true do begin

      if GlobalFree(RcvBufItem.hMemData) <> 0 then
        raise exception.create('GlobalFreeII(RcvBufItem.hMemData)) <> 0');

      if GlobalFree(RcvBufItem.hMemStruct) <> 0 then
        raise exception.create('GlobalFreeII(RcvBufItem.hMemStruct) <> 0');
    end else begin
      DoGarbageCollection;
      Waitforsingleobject(FMsgDispatchEvent, INFINITE);
    end;
  end;
end;
6. Die Daten aufbereiten, validieren und gegebenenfalls ungültigen Client disconnecten etc. usw.

Delphi-Quellcode:
procedure TCliCon.ProcessMessage(nMsgID: Byte; ptrData: Pointer; cbSize: DWORD);
var
  nKeepAlive: Byte;
  DataRec: TDataRec;
begin
  try
    if nMsgID = MSG_ID_DATA then begin
      try
        if Assigned(@ServerObj.CBData) then begin
          FillChar(DataRec, SizeOf(TDataRec), 0);
          DataRec.Socket := FSocket.SocketHandle;
          DataRec.ptrData := ptrData;
          DataRec.cbSize := cbSize;
          DataRec.ptrLogonSVRRec := @FLogonRec;
          ServerObj.CBData(@DataRec);
        end;
      except
        on e:exception do begin
           if Assigned(@ServerObj.CBClientError) then begin
             FillChar(DataRec, SizeOf(TDataRec), 0);
             DataRec.Socket := FSocket.SocketHandle;
             DataRec.Error := WSAGetLastError;
             DataRec.ErrorMsg := PChar(
               'EXCEPTION > $0228 [MSG_ID_DATA] : ' + e.Message
               );
             ServerObj.CBClientError(@DataRec);
           end;
           try FSocket.Close except end;
        end;
      end;
    end else
    if nMsgID = MSG_ID_LOGON then begin
      try
        move(ptrData^, FLogonRec, SizeOf(TLogonSVRRec));
        FLogonRec.ptrClientObj := FSocket;
        FStringHash.Add(FLogonRec.GUID, self);

        if Assigned(@ServerObj.CBLogon) then begin
          FillChar(DataRec, SizeOf(TDataRec), 0);
          DataRec.Socket := FSocket.SocketHandle;
          DataRec.ptrData := ptrData;
          DataRec.cbSize := cbSize;
          DataRec.ptrLogonSVRRec := @FLogonRec;
          ServerObj.CBLogon(@DataRec);
        end;
      except
        on e:exception do begin
           if Assigned(@ServerObj.CBClientError) then begin
             FillChar(DataRec, SizeOf(TDataRec), 0);
             DataRec.Socket := FSocket.SocketHandle;
             DataRec.Error := WSAGetLastError;
             DataRec.ErrorMsg := PChar(
               'EXCEPTION > $0227 [MSG_ID_LOGON] : ' + e.Message
               );
             ServerObj.CBClientError(@DataRec);
           end;
           try FSocket.Close except end;
        end;
      end;
    end else
    if nMsgID = MSG_ID_KEEP_ALLIVE then begin
      try
        //-- KeepAllive Retournieren
        SendMsg(MSG_ID_KEEP_ALLIVE, @nKeepAlive, SizeOf(Byte));
      except
        on e:exception do begin
           if Assigned(@ServerObj.CBClientError) then begin
             FillChar(DataRec, SizeOf(TDataRec), 0);
             DataRec.Socket := FSocket.SocketHandle;
             DataRec.Error := WSAGetLastError;
             DataRec.ErrorMsg := PChar(
               'EXCEPTION > $0228 [MSG_ID_KEEP_ALLIVE] : ' + e.Message
               );
             ServerObj.CBClientError(@DataRec);
           end;
           try FSocket.Close except end;
        end;
      end;
    end;
  except
    on e:exception do begin
       if Assigned(@ServerObj.CBClientError) then begin
         FillChar(DataRec, SizeOf(TDataRec), 0);
         DataRec.Socket := FSocket.SocketHandle;
         DataRec.Error := WSAGetLastError;
         DataRec.ErrorMsg := PChar(
           'EXCEPTION > TCliCon.ProcessMessage [GENERAL] : 1449 ' + e.Message
           );
         ServerObj.CBClientError(@DataRec);
       end;
    end;
  end;
end;

Prinzipiell wars das schon!

Beispielprogramm und Sourcen sind unter folgendem Thread verfügbar.

http://www.delphipraxis.net/internal...457&highlight=

Threaded-Buffered Async Socket (Client, Server Library incl. Source mit Demoprogramm)

lg.
  Mit Zitat antworten Zitat
Schwedenbitter

Registriert seit: 22. Mär 2003
Ort: Finsterwalde
622 Beiträge
 
Turbo Delphi für Win32
 
#8

Re: TServerSocket und Benutzeranmeldung

  Alt 27. Nov 2009, 17:05
1.
Also bzgl. des Header kann ich nur sagen: VOLLTREFFER!
Das ware genau das, was mir bei meiner Suche schon über den Weg gelaufen war und ich hatte es nicht aufgeschrieben/gespeichert etc. pp.

Den Quelltext habe ich mir angesehen. Da es ein vollständiges Programm ist, habe ich erhebliche Probleme, das zu verstehen. Ich habe aber Deine Hinweise zum Anlass genommen, selbst loszulegen und zu probieren. Also quasi learning by doing.

2.
Vom Client aus gesehen sieht es beim Connect nun so aus:
Delphi-Quellcode:
Procedure TForm1.SendMsg(Msg_ID: Byte; SData: String; Socket: TCustomWinSocket);
Var
   S            : String;
   Len         : DWord;
Begin
   S:='*****' + SData;
   Len:=Length(SData);
   Move(Msg_ID, S[1], 1);
   Move(Len,    S[2], 4);
   If Socket.SendText(S) < (Len + 5) Then
      ShowMessage(SysErrorMessage(GetLastError));
End;

procedure TForm1.Button1Click(Sender: TObject);
begin
   Client.Host:='localhost';
   Client.Open;
end;

Procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
Var
   S            : String;
Begin
   S:=Edit1.Text + #00 + Edit2.Text;
   SendMsg(USER_LOGON, S, Socket);
End;

End.
Beim Klick auf den Button wird die Verbindung geöffnet.
-> Daraufhin kommt das Ereignis OnClientConnect. Hier werden Benutzername und Passwort aus den TEdit-Feldern ausgelesen und mittels SendMsg an den Server geschickt. Die '*****' könnten auch was anderes sein. Sie sind nur Platzhalter für die Msg_ID und die Länge.

3.
Am Server sieht es dann so aus:
Delphi-Quellcode:
Procedure TForm1.ServerClientConnect(Sender: TObject; Socket: TCustomWinSocket);
Var
   Msg_ID      : Byte;
   Len         : DWord;
   I            : Integer;
   S            : String;
   LogonStr      : String;
Begin
   // Paket entpacken
   S:=Socket.ReceiveText;
   Move(S[1], Msg_ID,   1);
   Move(S[2], Len,      4);
   Delete(S, 1, 5);

   // Auf Logon-Daten prüfen
   If Msg_ID <> USER_LOGON Then Socket.Close            // unbekanntes Protokoll
   Else
   Begin
      LogonStr:=SepString(S, #00) + ':' + S;
      I:=UList.Items.IndexOf(LogonStr);
      If I = -1 Then Socket.Close                     // unbekannter Benutzer
      Else
      Begin
         UList.Items[I]:='@' + UList.Items[I];         // Online-Markierung
         UList.Sorted:=True;                           // Online nach oben         
      End;
   End;
End;
Im Grunde funktioniert das! Ich habe jetzt nur das Problem, wie ich die User verwalten kann. Denn ActiveConnections numeriert ja einfach durch. Damit kann ich es also nicht machen. Wie gesagt habe ich Probleme mit Deinem Beispielcode. Der ist mit Sicherheit sehr ausgeklügelt aufgeteilt (exen, dlls usw.). Aber ich komme damit nicht klar.

Merkst Du Dir die IP? Oder wie wird das gemacht?

Gruß, Alex
Alex Winzer
  Mit Zitat antworten Zitat
uoeb7gp
(Gast)

n/a Beiträge
 
#9

Re: TServerSocket und Benutzeranmeldung

  Alt 27. Nov 2009, 20:28
Hallo Schwedenbitter.

Also, die Clients kannst du so verwalten.

Delphi-Quellcode:
procedure TServerObj.ServerSocketClientConnect(const Sender: TObject;
  const Socket: TCustomWinSocket);
Diese Prozedure wird immer dann aufgerufen wenn sich ein Client zum Server verbindet.
Die Variable Socket identiffiziert den Client immer eindeutig.
Auf Grund dieser Eindeutigkeit kannst du dir für jede Clientverbindung eine Datenstruktur (ClientObjekt, Record etc.)
aufbauen. Dieses Clientobjekt speicherst du dir am besten in einer Ultra schnellen Hash Liste, da du bei jedem

Delphi-Quellcode:
procedure TServerObj.ServerSocketClientRead(const Sender: TObject;
  const Socket: TCustomWinSocket);
wieder an Hand des Socket das zugehörige Client - Objekt suchen musst.

Jedes Clientobjekt erhält ausserdem einen Empfangsbuffer, für die aus dem Socket gelesenen Daten.
Diese Aufteilung ist notwendig, da bei massivem Netzwerkverkehr die Datenpakete fragmentiert werden, und diese
Fragmente für jeden Client wieder zusammengesetzt werden müssen, solange bis die im Header angegebene Data-Size
für eine vollständige Übertragung erreicht ist, erst dann können die Daten weiterverarbeitet werden.

Hier ein Beispiel:

1. Client Connectet sich zum Server
2. Im ServerSocketClientConnect Event wird das Clientobjekt erzeugt.
3. ClientObjekt wird in einer Hashliste oder ähnlichen gespeichert.
4. Empfangen werden zB. 1020 Byte Daten (ServerSocketClientRead)
5. Hashliste wird mit der ID des sockets nach dem Clientobjekt durchsucht.
6. Die Empfangenen Daten werden in den Empfangsbuffer, des aus der Hashliste gesuchten, ClientObjektes geschrieben.
Analyse des Headers erfolgt.
Emfangsbuffer wird geprüft, ob die Datasize lt. Header Analyse schon vorhanden ist.
Wenn Ja, dann DataSize aus dem Buffer Extrahieren und weiter bei 7.
Wenn nein, alles von 1. bis Daten vollständig übertragen sind.
ACHTUNG wenn der Client sehr schnell viele Messages hintereinander zum Server sendet,
ist es möglich, dass der Empfangsbuffer auch mehrere vollständige und oder fragmentierte Datensätze enthält.
Dies musst du bei der Bufferanalyse (Datenextraction) auch noch berücksichtigen.
Hier musst du ordentlich Hirnschmalz hineinstecken!.
7. Message lt. ID im Header aufbereiten.


PS.
  • Meine Socket Dll's kapseln alles wie oben beschrieben, da brauchst du die eigntlich nur die Demos ansehen, wie
    diese zu verwenden sind.

Folgende Exportfunktionen (stdcall) sind vorhanden.
  • Client DLL:
SendData
Disconnect
Connect
  • Server DLL:
SendData
StopServer
StartServer

Kommunikation erfolgt mit Windowsmessages und oder Callbacks.

Hoffe dir damit geholfen zu haben.

lg.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:42 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz