Programowanie rozproszone
Laboratorium 4
Celem laboratorium było nabycie umiejętności programowania przesyłu komunikatów z użyciem gniazd (sockets).
#include <string.h> #include <arpa/inet.h> #include "sock.h" #include "sock.c" int main(int argc,char *argv[]){ int scid,i=0,j=0,size; char mbuffer[100]; char lbuffer[70]; struct sockaddr_in address; memset(&address,0,sizeof(address)); address.sin_family=AF_INET; address.sin_port=htons(17000); address.sin_addr.s_addr=inet_addr("149.156.136.215"); scid=sccreate(AF_INET, SOCK_STREAM, 0); if(scid==-1) exit(1); if(scconnect(scid,(struct sockaddr *) (&address), sizeof(address))==-1) exit(2); while(1) { fprintf(stdout,"Podaj komunikat do wyslania:\t"); if(fgets(lbuffer,sizeof(lbuffer),stdin)==NULL) fprintf(stderr,"fgets eof..."); size=strlen(lbuffer); mbuffer[size-1]='\0'; fprintf(stdout,"Komunikat do wyslania:\t\t%s\n",mbuffer); if(scsend(scid, &mbuffer, sizeof(mbuffer), 0)==-1) exit(3); if(mbuffer[0]=='0') { close(scid); fprintf(stdout,"Client disconnected\n"); return 0; } if(screcv(scid, &mbuffer, sizeof(mbuffer), 0)==-1) exit(4); fprintf(stdout,"%i: Odebrany komunikat:\t%s\n",getpid(),mbuffer); } return 0; } |
---|
#include <arpa/inet.h> #include "sock.h" #include "sock.c" char mbuffer[100]; void powme() { long int j = atoi(mbuffer); sprintf(mbuffer,"%ld",j*j); } int main(int argc,char *argv[]){ int scid, xscid; struct sockaddr_in address,xaddress; socklen_t address_len=sizeof(address); socklen_t xaddress_len=sizeof(xaddress); address.sin_family=AF_INET; address.sin_port=htons((short)17000); address.sin_addr.s_addr=INADDR_ANY; scid=sccreate(AF_INET, SOCK_STREAM, 0); if(scid==-1) exit(1); if(scbind(scid, (struct sockaddr *) (&address), address_len)==-1) exit(2); if(sclisten(scid, 10)==-1) exit(3); fprintf(stdout,"Server is up and running\n"); while(1) { if((xscid=scaccept(scid, (struct sockaddr *) &xaddress, &xaddress_len))==-1) exit(4); switch(fork()) { case -1: fprintf(stderr,"errno: %i : ",errno); perror("FORK ERROR"); break; case 0: close(scid); while(1) { if(screcv(xscid, &mbuffer, sizeof(mbuffer), 0)==-1) exit(5); fprintf(stdout,"Tresc otrzymanego komunikatu: %s\n",mbuffer); if(mbuffer[0]=='0') { fprintf(stdout,"Client terminate connection with server\n"); close(xscid); return 0; } powme(); fprintf(stdout,"Tresc wysylanego komunikatu: %s\n",mbuffer); if(scsend(xscid, &mbuffer, sizeof(mbuffer), 0)==-1) exit(6); } break; default: close(xscid); } } return 0; } |
---|
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <errno.h> #include <sys/socket.h> struct sockaddr_un { unsigned short sun_family; char sun_path[108]; }; int sccreate(int, int, int); int scconnect(int, const struct sockaddr *, socklen_t); int scbind(int, const struct sockaddr *, socklen_t); int scaccept(int, struct sockaddr *, socklen_t *); int sclisten(int, int); ssize_t screcv(int, void *, size_t, int); ssize_t scsend(int, const void *, size_t, int); |
---|
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <errno.h> #include <sys/socket.h> int sccreate(domain, type, protocol) { int scid; if((scid=socket(domain, type, protocol))==-1) { fprintf(stderr,"errno: %i : ",errno); perror("socket error"); } return scid; } int scconnect(int socket, const struct sockaddr *address, socklen_t address_len) { int value; if((value=connect(socket, address, address_len))==-1) { fprintf(stderr,"errno: %i : ",errno); perror("connect error"); } return value; } int scbind(int socket, const struct sockaddr *address, socklen_t address_len) { int value; if((value=bind(socket, address, address_len))==-1) { fprintf(stderr,"errno: %i : ",errno); perror("bind error"); } return value; } int scaccept(int socket, struct sockaddr *address, socklen_t *address_len) { int value; if((value=accept(socket, address, address_len))==-1) { fprintf(stderr,"errno: %i : ",errno); perror("accept error"); } return value; } int sclisten(socket, backlog) { int value; if((value=listen(socket, backlog))==-1) { fprintf(stderr,"errno: %i : ",errno); perror("listen error"); } return value; } ssize_t screcv(int socket, void *buffer, size_t length, int flags) { ssize_t value; int err; if((value=recv(socket, buffer, length, flags))==-1) { err=errno; fprintf(stderr,"%i-errno\n",errno); perror("recv error"); while(err==EINTR) { if((value=recv(socket, buffer, length, flags))==-1) { err=errno; perror("recv error - 2"); } else return value; } return -1; } return value; } ssize_t scsend(int socket, const void *buffer, size_t length, int flags) { int value, err; if((value=send(socket, buffer, length, flags))==-1) { err=errno; fprintf(stderr,"%i-errno\n",errno); perror("send error"); while(err==EINTR) { if((value=send(socket, buffer, length, flags))==-1) { err=errno; perror("send error - 2"); } else return value; } return -1; } return value; } |
---|
lunar@andLinux:~/windows/lab4$ ./lab4-klient Podaj komunikat do wyslania: 2 Komunikat do wyslania: 2 2730: Odebrany komunikat: 4 Podaj komunikat do wyslania: 81 Komunikat do wyslania: 81 2730: Odebrany komunikat: 6561 Podaj komunikat do wyslania: 43 Komunikat do wyslania: 43 2730: Odebrany komunikat: 1849 Podaj komunikat do wyslania: 0 Komunikat do wyslania: 0 Client disconnected |
---|
lunar@Moon:~ /lab4: ./lab4-serwer Server is up and running Tresc otrzymanego komunikatu: 2 Tresc wysylanego komunikatu: 4 Tresc otrzymanego komunikatu: 81 Tresc wysylanego komunikatu: 6561 Tresc otrzymanego komunikatu: 43 Tresc wysylanego komunikatu: 1849 Tresc otrzymanego komunikatu: 0 Client terminate connection with server |
Stworzona para programów (klient – serwer) pokazuje nam jak łatwo można przekazać wykonanie obliczeń na zewnętrzne maszyny (tym samym odciążenie stacji klienta), które w zamyśle mają do dyspozycji większą liczbę zasobów. Postępowanie takie rekompensuje nieraz nawet użycie mniej wydajnych algorytmów.
Podczas tego laboratorium używaliśmy gniazd i w przeciwieństwie do poprzednich zajęć, podczas których używaliśmy interfejsu MPI dostarczającego funkcje wysokiego poziomu, teraz używaliśmy podstawowych zapytań umożliwiających na gniazdach. Dzięki temu zagłębiliśmy się bardziej w implementacyjne szczegóły interakcji pomiędzy dwiema maszynami, a nie tylko ograniczyli się do wykorzystania gotowych interfejsów MPI, które przeprowadzały za nas całe zarządzanie gniazdami.