L'API Winsock
Pour programmer par Socket sous Delphi, on a recours aux fonctions de l'API Winsock.
La première fonction a utiliser impérativement est la fonction d'initialisation, propre à Windows : WSAStartup.
Sous Delphi, on pourra profiter de la présence de la partie Initialization des unites pour invoquer automatiquement cette fonction.
unit Sockets; interface uses Winsock; implementation procedure InitWinsock; var wsa: TWSAData; begin WSAStartup($101, wsa); // on réclame une pile IP de version 1.1 end; initialization InitWinsock; // Initialisation automatique finalization WSACleanup; // on ajoute aussi le cleanup end.
L'étape suivant va consister à ouvrir un socket, c'est ce qui va nous permettre de travailler avec l'ensemble des fonctions de l'API.
var so: TSocket; begin // so := socket(AF_INET, SOCK_DGRAM, IPROTO_UDP); pour un socket UDP so := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // pour un socket TCP if so = INVALID_SOCKET then Exit; // impossible d'allouer le socket ! end;
En cas d'erreur, c'est la fonction WSAGetLastError qui nous retournera le code erreur de la dernière fonction utilisée (il faut donc l'invoquer avant tout autre fonction de l'API sinon le code erreur sera perdu !).
Dans un premier temps, nous allons exploiter ce socket pour se connecter en TCP sur un serveur public : www.google.fr :)
var sa: TSockAddr; begin FillChar(sa, SizeOf(sa), 0); sa.sin_family := AF_INET; // connexion IP sa.sin_port := htons(80); // sur le port 80 sa.sin_addr.S_addr := INetAddr('www.google.fr'); // vers www.google.fr if sa.sin_addr.S_addr = INADDR_NONE then // adresse IP non trouvée Exit; if connect(so, sa, SizeOf(sa)) = SOCKET_ERROR then // erreur de connexion Exit; end;
J'utilise ici une fontion INetAddr de mon cru qui fonctionne aussi bien avec une adresse au format numérique qu'avec un nom d'hôte :
function INetAddr(const Host: string): Integer; var pHost: PChar; HostEnt: PHostEnt; begin if Host = '' then // juste au cas ou ... Result := INADDR_NONE else begin pHost := PChar(Host); Result := inet_addr(pHost); // est-ce une adresse au format x.y.z.w ? if Result = INADDR_NONE then begin HostEnt := gethostbyname(pHost); // est-ce un nom d'hote ? (résolution DNS) if HostEnt <> nil then Result := Integer(Pointer(HostEnt^.h_addr^)^); end; end; end;
Bien, maintenant que nous avons contacté le serveur et que celui a
Pour ce faire, nous allons utiliser une fonction qui permet d'envoyer une chaine de caractères sur un socket.
function SendStr(so: TSocket; const Str: string): Boolean; var l: Integer; x: Integer; i: Integer; begin Result := False; l := Length(Str); if l=0 then Exit; x := 1; while l > 0 do begin i := send(so, Str[x], l ,0); if i <= 0 then Exit; // 0 = déconnexion, si <0 alors erreur Winsock Dec(l, i); Inc(x, i); end; Result := True; end; begin SendStr(so, 'Allo ?'#13#10); // on ajoute un retour à la ligne sinon le serveur HTTP ne répondra pas end;
// cette fonction lit le socket pour remplir un cache function FillCache(so: TSocket; var Str: string): Boolean; var b: string; i: Integer; begin Result := False; SetLength(b, 1024); i := recv(so, b[1], 1024, 0); if i<=0 then Exit; Str := Str + copy(b, 1, i); Result := True; end; // cette fonction retourne la première ligne du cache se terminant par un CR/LF function ReadString(so: TSocket; var Cache, Str: string): Boolean; var i: Integer; begin Result := False; i := pos(#13#10, Cache); while i=0 do begin if not FillCache(so, Cache) then begin if Cache='' then Exit; i := length(cache); Break; end; i := pos(#13#10, Cache); end; Str := copy(Cache, 1, i-1); Delete(Cache, 1, i+1); Result := True; end; var cache: string; line : string; begin cache := ''; AllocConsole; // pour afficher une console depuis une application graphique while ReadString(so, cache, line) do WriteLn(Line); end;
Avec ça, on a déjà une réponse du serveur :
<html><head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>400 Bad Request</title>...
Bad Request :D et oui ! faut lui parler HTTP pour qu'il soit content :)
Date de dernière modification : 01/07/2009