Les sockets
Les sockets, ou Berkeley Sockets Interface, sont un ensemble normalisé de fonctions de communication développé par l'université de Berkeley
à partir des années 1980.
Cette interface existe dans pratiquement tous les langages de programmation, du C au C# en passant par Java.
Un socket représente une prise par laquelle une application peut envoyer et recevoir des données.
Cette prise permet à l'application de se brancher sur un réseau et communiquer avec d'autres applications.
Les piles TCP/IP sur Amiga
Voici la liste des quelques piles TCP/IP connues sur l'Amiga.
AmiTCP/IP AmiTCP-demo-40.lha sur Aminet
Miami (commercial)
Genesis (commercial)
RoadShow site de Olaf Barthel
Et aussi WinUAE, (c'est vrai, ce n'est pas une pile TCP/IP, c'est un émulateur...) mais en allant regarder dans Hardware, Expansion, Network on trouve la fameuse bsdsocket.library
et on peut utiliser la pile TCP/IP de Windows, la fameuse ws_32.dll alias Windows Socket 2.0 32-Bit DLL,
à travers la bsdsocket.library.
Petit programme
Pour illustrer le fonctionnement de ces sockets, rien de tel qu'un petit programme.
Il devrait compiler avec le SASC 6.0 et le netinclude
de l'AmigaOS4.0
/* * wget.c * récupère le contenu d'une url dans un fichier * exemple: wget www.google.com 80 /index.htm */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <proto/exec.h> #include <proto/bsdsocket.h> struct Library *SocketBase ; int main(int argc, char* argv[]) { struct sockaddr_in servaddr ; struct hostent *hp ; struct protoent *pe ; int sock_id ; char *message ; int msglen, msgcur, msgtodolen ; int err ; int port ; ULONG addr ; if (argc < 3) { printf("Usage: %s", argv[0]) ; exit(0) ; } port = atoi(argv[2]) ; if (port == 0) port = 80 ; message = malloc(2048) ; if (message == NULL) { printf("no mem\n") ; exit(0) ; } else { memset(message, 0, 2048) ; } SocketBase = OpenLibrary("bsdsocket.library", 0) ; if (SocketBase == NULL) { printf("no TCP/IP stack, no BSDsocket.library\n") ; exit(0) ; } else { printf("%s %ld.%ld\n", SocketBase->lib_Node.ln_Name, SocketBase->lib_Version, SocketBase->lib_Revision ) ; } pe = (struct protoent*)getprotobyname("tcp") ; if (pe == NULL) { printf("Cannot find protocol\n") ; exit(0) ; } else { printf("Got protocol \"%s\" %ld\n", pe->p_name, pe->p_proto) ; } /* Créé un socket */ sock_id = socket(AF_INET, SOCK_STREAM, pe->p_proto) ; if( sock_id == -1 ) { printf("Couldn't get a socket.\n") ; exit(EXIT_FAILURE) ; } else { printf("Got a socket.\n"); } memset(&servaddr, 0, sizeof(servaddr)) ; /* adresse du serveur distant */ hp = (struct hostent*) gethostbyname(argv[1]) ; if (hp == NULL) { printf("Couldn't get an address.\n") ; exit(EXIT_FAILURE) ; } else { /* là on aimerait bien écrire un truc comme **(UBYTE**)hp->h_addr, */ /* mais le 68000 n'est pas d'accord pour lire une adresse */ /* potentiellement impaire, alors on y va prudemment en lisant */ /* octet par octet */ UBYTE *p = (UBYTE *)hp->h_addr ; addr = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3] ; printf("Got an address.\n") ; printf("\"%s\" len=%ld %s\n", hp->h_name, hp->h_length, Inet_NtoA(addr)) ; } /* petit délire, rien que pour utiliser les fonctions de l'API bsdsocket... */ servaddr.sin_addr.s_addr = inet_addr((APTR)Inet_NtoA(addr)) ; /* numéro de port et type */ servaddr.sin_port = htons(port) ; servaddr.sin_family = AF_INET ; /* connexion */ err = connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) ; if (err != 0) { int i ; printf("Couldn't connect. %ld\n", err); printf("%02x", servaddr.sin_len) ; printf("%02x", servaddr.sin_family) ; for (i=0; i< 14; i++) { printf("%02x", ((struct sockaddr*)&servaddr)->sa_data[i]) ; } printf("\n") ; } else { printf("Got a connection!!!\n"); } if (err == 0) { /* Ouvrir un fichier pour conserver ce que l'on reçoit */ FILE *f= fopen("ram:bidule.bin", "wb") ; /* création d'une demande GET, avec tous les champs qu'il faut pour être poli avec le serveur */ sprintf( message, "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "From: nowhere\r\n" "User-Agent: %s\r\n\r\n", argv[3], /* l'url */ argv[1], /* le serveur */ argv[0]) ; /* le nom du programme */ msglen = 0 ; msgtodolen = strlen(message) ; msgcur = 0 ; /* Envoyer la demande GET */ do { msglen = send(sock_id, message+msgcur, msgtodolen, 0) ; if (msglen > 0) { msgcur += msglen ; msgtodolen -= msglen ; } } while ((msglen > 0) && (msgtodolen > 0)) ; /* Lire la réponse */ do { msglen = recv(sock_id, message, 2048, 0) ; if (msglen < 0) { } else if (msglen == 0) { } else { /* Ecrire un petit truc à l'écran (utile si la longueur de la réponse > 2Ko) */ printf("response (%ld bytes):%s", msglen, message); if (f != NULL) { /* Ecriture dans le fichier ram:bidule.bin */ fwrite(message, msglen, 1, f) ; } } } while (msglen > 0) ; if (f != NULL) { /* Fermeture du fichier ram:bidule.bin */ fclose(f) ; } } CloseSocket(sock_id) ; free(message) ; CloseLibrary(SocketBase) ; return 0 ; }
Makefile
Le petit programme ne compile pas ?
Dans la user-startup ou la startup-sequence il devrait y avoir la définition des includes pour le SAS C à l'aide de la commande assign.
assign INCLUDE: dev:sc/include
ajouter
assign NETINCLUDE: dev:sdks/sdk_53.30/netinclude
Ce fameux netinclude se trouve dans SDK_Install\base.lha\Include\ du sdk_53.30.lha
Après avoir recopié le fichier source wget.c dans un répertoire (nommé wget aussi, il faut savoir ranger ses programmes :) ), éditer un fichier makefile comme suit:
target: wget wget.o: wget.c sc INCLUDEDIR=NETINCLUDE: OBJNAME=wget.o wget.c wget: wget.o slink LIB:c.o,wget.o TO wget LIB LIB:sc.lib NOICONSDe cette manière, le pré-compilateur ira chercher dans NETINCLUDE: puis dans INCLUDE: s'il ne trouve pas son include.
Essais
wget www.google.com 80 /index.htm
Une belle erreur 404... alors que 10 ans en arrière il rendait quelque chose de mieux.
De mieux en mieux...
wget weather.service.msn.com 80 /data.aspx?outputview=search&weasearchstr=Grenoble&culture=fr-FR&src=wget
Vous donnera sûrement la météo dans les environs de Grenoble...
Url or not url ?
wget weather.service.msn.com 80 /data.aspx?outputview=search&weasearchstr=Grenoble&culture=fr-FR&src=wgetest l’équivalent de la saisie de l’url (URL = Uniform Resource Locator) suivante dans la barre d’adresse d’un navigateur
http://weather.service.msn.com/data.aspx?outputview=search&weasearchstr=Grenoble&culture=fr-FR&src=wgetVous remarquerez que l’on est en http. lien ici.
Conclusion
On est loin d'un navigateur, mais on est très proche d'une commande wget.
J'espère vous avoir donné l'envie d'utiliser le SAS/C, même s'il date, il m'a rendu de bons services.
Voilà de quoi bien occuper ses soirées, sachant que le code donné en exemple fonctionne en mode 68000 et aussi sous WB1.3.
Il n'existe pas de pile TCP/IP pour 68000 et WB1.3, seul WinUAE le rend possible... car tout le gros du travail se passe
du coté Windows.