Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Chat Server empfängt immer nur einmal (https://www.delphipraxis.net/42432-chat-server-empfaengt-immer-nur-einmal.html)

Luckie 18. Mär 2005 17:25


Chat Server empfängt immer nur einmal
 
Ich bin gerade dabei einen kleinen Chat mit Server und Clients zu programmieren. Dabei benutze ich keine Komponenten. Der Server wartet dabei in einem Thread auf Clients, die sich verbinden. Jeder Client kommt in ein Array. Dann geht der Server im Thread jedes mal dieses Array durch und guckt, ob vom entsprechenden Client was kommt:
Delphi-Quellcode:
function ServerThread(p: Pointer): Integer;
var
  TempConSocket: TSocket;
  i: Integer;
  MsgBuffer: array[0..255] of Char;
resourcestring
  rsListen = 'Listening...';
  rsSocketOK = 'Neuer Client hat sich verbunden.';
  rsSocketError = 'Ungültige Socket';
  rsMsg = 'Client: %s';
begin
  bRunning := True;
  if listen(acceptSock, 10) <> SOCKET_ERROR then
  begin
    AddLogText(hApp, IDC_LV, rsListen, 2);
    while bRunning do
    begin
      listen(acceptSock, 10);
      TempConSocket := accept(acceptSock, nil, nil);
      if TempConSocket <> INVALID_SOCKET then
      begin
        SetLength(conSocket, length(conSocket)+1);
        conSocket[High(conSocket)] := TempConSocket;
        AddLogText(hApp, IDC_LV, rsSocketOK, 2);
      end
      else
        AddLogText(hApp, IDC_LV, rsSocketError, 0);
      for i := 0 to length(conSocket) - 1 do
      begin
        if recv(conSocket[i], MsgBuffer, sizeof(MsgBuffer), 0) > 0 then
          AddLogText(hApp, IDC_LV, Format(rsMsg, [String(MsgBuffer)]), 2);
        Zeromemory(@MsgBuffer, sizeof(MsgBuffer));
      end;
    end;
  end
  else
    AddLogText(hApp, IDC_LV, SysErrorMessage(WSAGetLastError), 0);
  result := 0;
end;
Aber irgendwie kann ich nur einmal was an den Server senden. Alles weitere kommt nicht an. irgendwo, muss da ein Denkfehler von mir sein. :gruebel:

Jens Schumann 18. Mär 2005 17:37

Re: Chat Server empfängt immer nur einmal
 
Hallo Luckie
Zitat:

Zitat von Luckie
if recv(conSocket[i], MsgBuffer, sizeof(MsgBuffer), 0) > 0 then
AddLogText(hApp, IDC_LV, Format(rsMsg, [String(MsgBuffer)]), 2);...

Ich habe zwar noch nie mit den API's programmiert, aber es sieht so aus, als ob hier aus der Verbindung gelesen wird. Aber nur einmal. Kann es sein, dass das was Du sendest in mehrere Pakte aufgeteilt wird und nur das erste Paket aus der Verbindung liest? Evt. solltest solange aus der Verbindung lesen bis recv 0 zurückliefert.

Basilikum 18. Mär 2005 19:42

Re: Chat Server empfängt immer nur einmal
 
du verwendest die Sockets im Blocking-Mode, sprich:

Thread startet, listen macht Port auf, accept blockiert, bis eine Connection eingeht, danach blockiert recv, bis das erste TCP-Segment eintrifft, danach wieder accept, welches aber blockiert, bis eine Connection eingeht, danach blo.... while true do ; :-)

entweder lässt du einen Thread als Listener laufen, der dann pro akzeptierter Connection einen weiteren Thread spawned...
oder du betreibst die Sockets im Non-Blocking-Mode.... mehr dazu in der WinSock-Referenz (Stichworte: select / ioctlsocket)

Luckie 18. Mär 2005 22:55

Re: Chat Server empfängt immer nur einmal
 
Aaaah. Danke, das war meine Wissenslücke. Ich habe jetzt für jeden neuen Client einen eigenen Thread. Nur habe ich jetzt ein konzeptionelles Problem:
Delphi-Quellcode:
function ClientThread(p: Pointer): Integer;
var
  Sock: TSocket;
  MsgBuffer: array[0..255] of Char;
resourcestring
  rsMsg = 'Client: %s';
begin
  Sock := PClientThreadParams(p)^.Sock;
  while True do
  begin
    if recv(Sock, MsgBuffer, sizeof(MsgBuffer), 0) > 0 then
      AddLogText(hApp, IDC_LV, Format(rsMsg, [string(MsgBuffer)]), 2);
  end;
  FreeMem(p, sizeof(TClientThreadParams));
end;
Ich habe jetzt alle Nachrichten, die ankommen, "auf dem Server". Wie verteile ich sie jetzt am besten wieder auf die Clients? :gruebel:

Basilikum 19. Mär 2005 00:01

Re: Chat Server empfängt immer nur einmal
 
vermutlich wirst du nicht um den Non-Blocking-Mode herumkommen.... und für die Kommunikation zwischen den Client-Threads wird irgend eine Art von Thread-Safen Queue benötigt...

pseudo-code:
Code:
while ClientConnected do begin

  if networkDataAvailable then pushQueueData

  if queueDataAvailable then sendNetworkData

end;
(wobei die *DataAvailable für beispielsweise 200ms blockieren, wenn keine Daten vorhanden sind, so dass nicht die gesamte CPU beübt wird.... die daraus resultierende Latenz ist bei einem Chat-System nicht störend...)

auf Dinge wie concurrent Socket-Operationen mehrere Threads auf dem selben Socket würde ich mich nicht einlassen... das kracht über kurz oder lang...

PS: hier spielst du mit dem Feuer (access violation / live lock):
Delphi-Quellcode:
while True do
  begin
    if recv(Sock, MsgBuffer, sizeof(MsgBuffer), 0) > 0 then
      AddLogText(hApp, IDC_LV, Format(rsMsg, [string(MsgBuffer)]), 2);
  end;
besser so:
Delphi-Quellcode:
while true do begin
  r:=recv(Sock, MsgBuffer, sizeof(MsgBuffer) - 1, 0); // #0 muss auch Platz haben
  if not (r > 0) then break; // recv gibt 0 zurück, wenn Connection geschlossen wurde

  MsgBuffer[r]:=#0;
  AddLogText(hApp, IDC_LV, Format(rsMsg, [string(MsgBuffer)]), 2);
end;

Luckie 19. Mär 2005 00:15

Re: Chat Server empfängt immer nur einmal
 
Zitat:

Zitat von Basilikum
vermutlich wirst du nicht um den Non-Blocking-Mode herumkommen....

Ich hatte gerade, als ich du das über blocking und non-blocking geschrieben hast, etwas im Internet gestöbert. da wurde allgemein gesagt, dass man non-blocking sockets nicht verwenden sollte.

Zitat:

und für die Kommunikation zwischen den Client-Threads wird irgend eine Art von Thread-Safen Queue benötigt...
Ich habe mir das so überlegt: Die Nachricht wird in einen gloablen String geschrieben. Jedesmal, wenn was ankommt, feuert der ClientThread einen Event und der ServerThread, der die ClientThreads erstellt, schickt die Nachricht dann Reih um an alle Clients. Das heißt, ich bräuchte zusätzlich noch ein Array mit den SocketASdressen der Clients. Oder könnte man auch auf dem Port einen Broadcast schicken? Wenn ja, wie geht das?
`
Zitat:

auf Dinge wie concurrent Socket-Operationen mehrere Threads auf dem selben Socket würde ich mich nicht einlassen... das kracht über kurz oder lang...
Kannst du das etwas näher ausführen? das sind so meine ersten Schritte in der Netzwerkprogrammierung.

Zitat:

PS: hier spielst du mit dem Feuer (access violation / live lock):
Delphi-Quellcode:
while True do
  begin
    if recv(Sock, MsgBuffer, sizeof(MsgBuffer), 0) > 0 then
      AddLogText(hApp, IDC_LV, Format(rsMsg, [string(MsgBuffer)]), 2);
  end;
besser so:
Delphi-Quellcode:
while true do begin
  r:=recv(Sock, MsgBuffer, sizeof(MsgBuffer) - 1, 0); // #0 muss auch Platz haben
  if not (r > 0) then break; // recv gibt 0 zurück, wenn Connection geschlossen wurde

  MsgBuffer[r]:=#0;
  AddLogText(hApp, IDC_LV, Format(rsMsg, [string(MsgBuffer)]), 2);
end;

Ups. :oops: Danke.

Basilikum 19. Mär 2005 08:13

Re: Chat Server empfängt immer nur einmal
 
Zitat:

Zitat von Luckie
Zitat:

Zitat von Basilikum
vermutlich wirst du nicht um den Non-Blocking-Mode herumkommen....

Ich hatte gerade, als ich du das über blocking und non-blocking geschrieben hast, etwas im Internet gestöbert. da wurde allgemein gesagt, dass man non-blocking sockets nicht verwenden sollte.

das würde mich etwas erstaunen, eine solche Pauschal-Aussage... vermutlich unter bestimmten Bedingungen, oder ?

Zitat:

Zitat von Luckie
Zitat:

und für die Kommunikation zwischen den Client-Threads wird irgend eine Art von Thread-Safen Queue benötigt...
Ich habe mir das so überlegt: Die Nachricht wird in einen gloablen String geschrieben. Jedesmal, wenn was ankommt, feuert der ClientThread einen Event und der ServerThread, der die ClientThreads erstellt, schickt die Nachricht dann Reih um an alle Clients. Das heißt, ich bräuchte zusätzlich noch ein Array mit den SocketASdressen der Clients. Oder könnte man auch auf dem Port einen Broadcast schicken? Wenn ja, wie geht das?

das meinte ich mit den concurrent Socket-Operationen: einerseite würde ja der Client-Thread im recv warten, und der Server versucht gleichzeitig noch per send (auf dem selben Socket) Daten abzusetzen.
"Port Broadcasts" gibts nicht.

als Quit'n'Dirty Variante könntest du die Windows-Message-Queue missbrauchen (PostThreadMessage, etc). So must du nicht selber eine thread-safe queue implementieren.
und innerhalb der Client-Threads mit select() für einige 100ms auf Netzwerk-Daten warten, dann kurz die Window-Message-Queue prüfen, danach wieder auf Netzwerk-Daten warten. Du brauchst dann lediglich ein Array auf alle Thread-IDs, die allerdings wieder thread-safe sein muss....

Zitat:

Zitat von Luckie
Zitat:

auf Dinge wie concurrent Socket-Operationen mehrere Threads auf dem selben Socket würde ich mich nicht einlassen... das kracht über kurz oder lang...
Kannst du das etwas näher ausführen? das sind so meine ersten Schritte in der Netzwerkprogrammierung.

siehe oben :-)

ein grundlegend anderer Ansatz wäre, alle Sockets mit einem einzigen Thread zu bedienen:
- Socket auf non-blocking schalten
- mittels select() auf ein Ereignis (eingehende Verbindung oder Daten vorhanden) eines der vorhandenen Sockets (Server-/Client-Sockets) warten
- entsprechend reagieren (Connection akzepiteren, Daten lesen und weitersenden)

würde ich jedoch nicht verwenden:
- keine saubere Kapselung der Clients gegeneinander
- riesiger Thread-Code, unübersichtlich, fehleranfällig


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:27 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