Newsgroup: it.comp.lang.delphi
Titolo: Socket e thread - DuplicateHandle

Avrei bisogno di avere un socket server tcp in ascolto sul thread
principale,  poi ad ogni connessione creo un nuovo thread e qui dovrei avere
un altro socket che gestisce i dati che gli arrivano.
Non voglio usare componenti server che hanno già al loro interno i thread in
quanto il thread me lo creo io in base alle esigenze.
Utilizzando il componente SimpleTCP sono riuscito a creare il tutto e
funziona fintanto che uso una connessione telnet.
Attraverso il debugger vedo che il thread principale riceve la connessione e
poi una volta che viene passata ai thread secondari questi lavorano in modo
indipendente.
Il problema sorge quando dall'altra parte ho un programma (in python su
linux) che non appena stabilita la connessione mi invia dei dati (per
scrupolo ho anche provato a mettere una sleep di un secondo sul programma
python tra la connessione e l'invio dati ma non è cambiato nulla).
Succede che alla connessione, mi viene richiamato l'evento onaccept che mi
crea il thread e duplica il socket, soltanto che i dati arrivano sul socket
server principale e non a quello dentro al thread.
Se metto dei breackpoint ed eseguo le varie istruzioni passo passo anche
velocemente il programma funziona.
Presumo che il problema sia nella DuplicateHandle ma non so da dove partire.
Allego un po' di codice:
**** Procedura accept del componente server principale ****
procedure TSimpleTCPServer.DoAccept;
var
  Tmp, Error: Integer;
  tmpSocket: TSocket;
  tmpTCPClient: TSimpleTCPClient;
  IsAccept: Boolean;
begin
  Tmp := SizeOf(SockAddrIn);
  tmpSocket := WinSock.Accept(FSocket, @SockAddrIn, @Tmp);
  Error := WSAGetLastError;
  if (tmpSocket = SOCKET_ERROR) then
    if (Error <> WSAEWOULDBLOCK) then
    begin
      SocketError(tmpSocket, Error)
    end
    else
    begin
      Exit;
    end;
  tmpTCPClient := TSimpleTCPClient.Create(nil);
  tmpTCPClient.FSocket := tmpSocket;
  tmpTCPClient.FHost := inet_ntoa(SockAddrIn.SIn_Addr);
  tmpTCPClient.FPort := FPort;
  tmpTCPClient.FConnected := True;
  if Assigned(FOnAccept) then
  begin
    IsAccept := True;
    FOnAccept(Self, tmpTCPClient, IsAccept);
    if IsAccept then
      Connections.Add(tmpTCPClient)
    else
      tmpTCPClient.Free;
   end
  else
   Connections.Add(tmpTCPClient);
  if Assigned(FOnClientConnected) then
    FOnClientConnected(Self, tmpTCPClient);
end;
**** Nella FOnAccept viene eseguito il mio codice che crea il thread ****
procedure TMainForm.ServerSocketAccept(Sender: TObject;
  Client: TSimpleTCPClient; var Accept: Boolean);
var PartProgramThread: TPartProgramThread;
  Resumed: Boolean;
begin
  if Client.Socket <> FUltimoSocket
  then
  begin
    FUltimoSocket := Client.Socket;
    Resumed := False;
    PartProgramThread := TPartProgramThread.Create(Client);
    try
      PartProgramThread.FreeOnTerminate := False;
      PartProgramThread.DbAlias := 'Database';
      PartProgramThread.DbUser := 'sa';
      PartProgramThread.DbPassword := 'password';
      PartProgramThread.HandlePadre := Handle;
      PartProgramThread.RemoteHost := Client.Host;
      PartProgramThread.Resume;
      Resumed := True;
      //Aspetto che sia finita la procedura OnStartinThread
      PartProgramThread.WaitForStart(INFINITE);
      ListaPartProgramThread.Add(PartProgramThread);
    except
      if Resumed
      then
      begin
        PartProgramThread.Terminate;
        PartProgramThread.WaitFor;
      end;
      PartProgramThread.Free;
    end;
  end;
end;
**** Questo pezzo viene eseguito all'interno dell'execute del thread ****
procedure TPartProgramThread.DoStartingThread;
begin
  inherited;
  FSocket := TSimpleTCPClient.Create(nil);
  FSocket.OnRead := ClientSocketRead;
  FSocket.OnError := ClientSocketError;
  FSocket.OnDisconnected := ClientSocketDisconnected;
  DuplicaSocket(FSocketPartenza);
  SendLogMessage(Format('Thread partito per host %s.', [RemoteHost]));
end;
procedure TPartProgramThread.DuplicaSocket(
  ClientSocket: TSimpleTCPClient);
var
  NewHandle: Integer;
begin
  if not (DuplicateHandle(GetCurrentProcess,
    ClientSocket.Socket,
//        Self.Handle,
//        GetCurrentThread,
    GetCurrentProcess, @NewHandle, 0, True, DUPLICATE_SAME_ACCESS))
  then
  begin
    raise Exception.CreateFmt('Errore nella duplicazione del socket: %s',
      [SysErrorMessage(GetLastError)]);
  end;
  FSocket.DuplicateSocket := NewHandle;
  FSocket.Connected := True;
end;
**** Alla fine nel socket all'interno del thread quando viene impostato come
connected a true succede questo: ****
procedure TSimpleTCPClient.SetConnected(Value: Boolean);
var
  lin: TLinger;
  linx: Array[0..3] of Char absolute lin;
  ErrorCode: Integer;
begin
  if not (csDesigning in ComponentState) then
   if FConditionallyConnected <> Value then
    begin
     FConditionallyConnected := Value;
     if Value then
      begin
       if DuplicateSocket > 0
       then
       begin
         SockAddrIn.sin_family := AF_INET;
         SockAddrIn.sin_port := htons(FPort);
         SockAddrIn.sin_addr.s_addr := inet_addr(PChar(Host));
         if SockAddrIn.sin_addr.s_addr = -1 then
          begin
           HostEnt := GetHostByName(PChar(Host));
           if HostEnt = nil then
            begin
             SocketError(INVALID_SOCKET, WSAEFAULT);
             Exit;
            end;
           SockAddrIn.sin_addr.S_addr :=
LongInt(PLongInt(HostEnt^.h_addr_list^)^);
          end;
         FSocket := DuplicateSocket;
         ErrorCode := WSAASyncSelect(FSocket, WindowHandle,
UM_TCPASYNCSELECT,
                                     FD_READ or FD_CONNECT or FD_CLOSE);
         if ErrorCode <> 0 then
          begin
           SocketError(FSocket, WSAGetLastError);
           Exit;
          end;
         DoConnect;
       end
       else
       begin
         SockAddrIn.sin_family := AF_INET;
         SockAddrIn.sin_port := htons(FPort);
         SockAddrIn.sin_addr.s_addr := inet_addr(PChar(Host));
         if SockAddrIn.sin_addr.s_addr = -1 then
          begin
           HostEnt := GetHostByName(PChar(Host));
           if HostEnt = nil then
            begin
             SocketError(INVALID_SOCKET, WSAEFAULT);
             Exit;
            end;
           SockAddrIn.sin_addr.S_addr :=
LongInt(PLongInt(HostEnt^.h_addr_list^)^);
          end;
          FSocket := WinSock.Socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
         if FSocket = SOCKET_ERROR then
          begin
           SocketError(INVALID_SOCKET, WSAGetLastError);
           Exit;
          end;
         ErrorCode := WSAASyncSelect(FSocket, WindowHandle,
UM_TCPASYNCSELECT,
                                     FD_READ or FD_CONNECT or FD_CLOSE);
         if ErrorCode <> 0 then
          begin
           SocketError(FSocket, WSAGetLastError);
           Exit;
          end;
         ErrorCode := WinSock.Connect(FSocket, SockAddrIn,
SizeOf(SockAddrIn));
         if ErrorCode <> 0 then
          begin
           ErrorCode := WSAGetLastError;
           if ErrorCode <> WSAEWOULDBLOCK then
            begin
             SocketError(FSocket, WSAGetLastError);
             Exit;
            end;
          end;
        end
      end
     else
      begin
       WSAASyncSelect(FSocket, WindowHandle, UM_TCPASYNCSELECT, 0);
       Shutdown(FSocket, 2);
       lin.l_onoff := 1;
       lin.l_linger := 0;
       SetSockOpt(FSocket, SOL_SOCKET, SO_LINGER, linx, SizeOf(Lin));
       ErrorCode := CloseSocket(FSocket);
       if ErrorCode <> 0 then
        begin
         SocketError(FSocket, WSAGetLastError);
         Exit;
        end;
       FSocket := INVALID_SOCKET;
       FConnected := False;
       if Assigned(FOnDisconnected) and not (csDestroying in ComponentState)
then
         FOnDisconnected(Self);
      end;
    end
   else
  else
   if Value then
    raise Exception.Create('Can not connect at design-time');
end;
Qualche idea?
Grazie.

 in  quanto il thread me lo creo io in base alle esigenze.
Posso chiederti perché ?
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 ha scritto nel messaggio 
news:eebcvr$bvb$1...@newsreader.mailgate.org...

Forse il componente socket che stai usando e' Asincrono cosicche' in debug 
funziona, in quanto le funzioni fanno in tempo a compiersi?
Ciao Giorgio 

 Posso chiederti perché ?
Il motivo principale è che dentro al thread eseguo delle query, mando
messaggi di win ecc. Quando poi io ad esempio ricevo dei dati tramite
socket, voglio essere sicuro che l'evento scatenato alla ricezione dei dati
e di conseguenza il codice che inserisco, sia eseguita nel contesto del mio
thread e non in un altro parallelo che potrebbe generarmi delle deadlock e
altri problemi legati all'accesso contemporaneo alle risorse.


Da quello che ho visto il componente durante il resto del funzionamento si
comporta nel modo giusto.
Il problema ce l'ho solo se mentre sto facendo connessione ho già dei dati
pronti ad arrivarmi.
Dall'ultima prova che ho fatto, facendo girare il programma senza breakpoint
e mettendo sul programma python una sleep di un secondo tra la connect e
l'invio dei dati adesso funziona.
Comunque è una soluzione che non mi piace.

 ha scritto nel messaggio 
news:eee0qc$vde$1...@newsreader.mailgate.org...

Da quello che ho visto in effetti il componente socket che usi nel Thread e' 
asincrono (non bloccante), abbinamento che e' fortemente sconsigliato.
Il fatto strano e' anche che il primo read dai dati venga effettuato dal 
socket Server, il che mi fa pensare ad un errore di progettazione del 
TSimpleTCPServer:
nel suo codice cerca il comando WSAAsyncSelect, se tra i parametri c'e' un 
FD_READ toglillo.
Ciao Giorgio 

altri problemi legati all'accesso contemporaneo alle risorse.
Usa gli Indy: tutto questo ti viene garantito ed č gią pronto e
funzionante.

 c'e' un FD_READ toglillo.
Infatti, ho individuato due WSAAsyncSelect per l'apertura, una sulla listen
e un'altra sulla connect. Entrambe avevano l'FD_READ. Tolto da quella che
metteva il socket in listen il componente ha cominciato a funzionare a
dovere.
Grazie mille.
Samuele

 Infatti, ho individuato due WSAAsyncSelect per l'apertura[...]
..reinventiamo pure la ruota... ;-)
