program serv; {$APPTYPE CONSOLE} uses dialogs, Classes, SysUtils, Windows, Winsock; var wsaData :TWSAData; server, client :TSocket; SockAddr :TSockAddr; list_Clients:TList; SockSize, ClientCount, num_client :integer; cs : TRTLCriticalSection; DispathThreadid: cardinal; Type TClient=class Handle :TSocket; // le socket client Thread :THandle; // Thread de communication Address, // adresse IP du client Buffer, // tampon de lecture du socket msg :string; num :integer; go : boolean; Constructor Create(i:integer); Destructor Destroy; override; procedure Exec; procedure ReadLine; procedure SendLine(s:string); end; var t, f : textfile; tmp1, tmp2 : boolean; procedure FatalError(msg:String); begin WriteLn(msg); ReadLn; halt; end; function ClientThread(Sender:TClient):integer; stdcall; begin Result:=0; Try Sender.Exec; finally Sender.Free; end; end; procedure Dispath(s:string); var k,l,m:integer; v: boolean; begin s:=s+#13#10; k:=0; l:= list_Clients.Count; v:=true; while v do begin // Procedure de relecture if l>k then begin // de la liste des clients m:=l-1; // en cas de changement de for m:=k to m do begin // la TList Clients... k:=m; // Verification de la validitée du thread client avant envoie if assigned(TClient(list_Clients[m])) then TClient(list_Clients[m]).SendLine(s); // Cette procedure evite end; // la section critique qui l:=list_Clients.count; // bloquais tout les autres k:=k+1; // threads ki voulaient acceder end // a la TList, ou le bloquais else v:=false; // si un autre utilisais la TList... end; end; function DispathThread(p:pcardinal):integer; var temps, freq : single; i : integer; s : string; begin freq := 0.1 *0.00001; temps := time; while client<>INVALID_SOCKET do begin if time - temps > freq then begin temps := temps + freq; s := ''; write(t,0); EnterCriticalSection(cs); write(t,1); if list_Clients.count<>0 then begin for i:=0 to list_Clients.count - 1 do begin tmp1 := (TClient(list_Clients[i]).go); tmp2 := (TClient(list_Clients[i]).msg <> ''); if tmp1 and tmp2 then begin s := s + 'j;' + inttostr(TClient(list_Clients[i]).num); s := s + ';' + TClient(list_Clients[i]).msg; end; end; Dispath(s); end; LeaveCriticalSection(cs); end else sleep(1); end; Result := 0; writeln('AAAAAAAAAAAAAAAAAAAAAAAAA'); writeln('AAAAAAAAAAAAAAAAAAAAAAAAA'); writeln('AAAAAAAAAAAAAAAAAAAAAAAAA'); writeln('AAAAAAAAAAAAAAAAAAAAAAAAA'); readln; end; Constructor TClient.Create(i:integer); var id : Cardinal; s : string; begin Handle:=Client; // on conserve le numéro de socket Address:=inet_ntoa(SockAddr.sin_addr); // on récupère son adresse IP self.num := i; SendLine('s;'+inttostr(i)+';'); EnterCriticalSection(cs); try list_Clients.Add(Self); writeln(address,' : nouvelle connection - ',list_Clients.Count,' joueur(s) '); s := ''; for i:=0 to list_Clients.count - 1 do begin s := s + 'c;' + inttostr(TClient(list_Clients[i]).num) + ';'; end; dispath(s); finally LeaveCriticalSection(cs); end; Thread:=CreateThread(nil,0,@ClientThread,Self,0,id); // lancement d'un Thread client end; Destructor TClient.Destroy; begin try dispath('d;'+inttostr(self.num)+';'); finally end; shutdown(Handle,SD_BOTH); closesocket(Handle); EnterCriticalSection(cs); try list_Clients.Remove(Self); finally LeaveCriticalSection(cs); end; CloseHandle(Thread); end; Procedure TClient.Exec; var s:string; begin s:=''; go := true; try while go do ReadLine; except on e:Exception do begin // en cas de bugs suite a des exeptions // décommentez les lignes ci dessous // malgrès tout mes tests, un bug peut tout de même subvenir... EnterCriticalSection(cs); try WriteLn(Address,',',num,' : ',e.Message); WriteLn(t, Address,',',num,' : ',e.Message); WriteLn(f, Address,',',num,' : ',e.Message); finally LeaveCriticalSection(cs); end; end; end; end; procedure TClient.ReadLine; var temp:string; i:integer; hqegdl:string; begin // lecture du socket i:=pos(#13#10,Buffer); while i=0 do begin SetLength(temp,1024); i:=recv(Handle,Temp[1],1024,0); if i<=0 then raise Exception.Create('fin de communication'); Buffer:=Buffer+Copy(Temp,1,i); i:=pos(#13#10,Buffer); if (i=0)and(Length(Buffer)>80) then raise Exception.Create('client trop bavard !');// limiter la taille du buffer interne ! end; EnterCriticalSection(cs); try write(f,1); hqegdl:=Copy(Buffer,1,i-1); write(f,2); write(f,'(',self.msg,')'); write(f,'(',hqegdl,')'); write(f,3); self.msg:=hqegdl; write(f,4); //finally except on e:exception do writeln(f,e.Message); on e:EAccessViolation do writeln(f,e.Message); end; LeaveCriticalSection(cs); Delete(Buffer,1,i+1); end; procedure TClient.SendLine(s:string); var i:integer; begin {Regarde si le socket est encore valide si oui le WSAGetLastError retourne 0 si non il retourne le codde d'erreur (10054 par exemple) pour connaitre les erreurs winsock, référez vous a ma source "Erreur Winsock" sur www.delphifr.com} i:=WSAGetLastError; if i=0 then send(Handle,s[1],length(s),0) else raise Exception.Create('Erreur n : '+inttostr(i)); end; procedure init; begin AssignFile(t, 'log.txt'); rewrite(t); AssignFile(f, 'log2.txt'); rewrite(f); WriteLn('Ctrl+C pour fermer'); if wsaStartup($101,wsaData)<>0 then FatalError('Erreur winsock'); // Initialisation de WINSOCK server:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // allocation d'un socket TCP if server=INVALID_SOCKET then FatalError('Impossible d''allouer un socket' ); FillChar(SockAddr,SizeOf(SockAddr),0); SockAddr.sa_family:=AF_INET; SockAddr.sin_port :=htons(4269); // déclarer le port 4269 if bind(server,SockAddr,SizeOf(SockAddr))<>0 then FatalError('Impossible de démarrer le service 4269'); if listen(server,0)<>0 then FatalError('Démarrage du serveur impossible') else writeln('serveur en place'); // écoute les clients list_Clients:=TList.Create; // la liste des client SockSize:=SizeOf(SockAddr); // boucle principale client:=accept(server,@SockAddr,@SockSize); num_client := 0; initializeCriticalSection(cs); CreateThread(nil,0,@DispathThread,@DispathThreadid,0,DispathThreadid); while client<>INVALID_SOCKET do begin num_client := num_client + 1; TClient.Create(num_client); client:=accept(server,@SockAddr,@SockSize); end; shutdown(server,SD_BOTH); // fermeture du socket serveur (implique la déconnexion de tous les clients) closesocket(server); repeat // attend la fermeture des connexions clientes Sleep(1); // pour ne pas saturer la machine ! EnterCriticalSection(cs); try ClientCount:=list_Clients.Count; finally LeaveCriticalSection(cs); end; until ClientCount=0; DeleteCriticalSection(cs); wsaCleanup; // Finalisation de WINSOCK writeln('serveur hs'); ReadLn; end; begin init; end.