/* * Dinko Korunic, kreator@fly.srk.fer.hr, 36355514 * Thu Dec 13 21:14:53 CET 2001 * * Potrebno je napraviti jednostavni echo klijent i posluzitelj ovaj puta * koristeci TCP protokol kako je to opisano u gornjem tekstu. Sve je isto * kao i u prethodnoj vjezbi osim sto ovaj puta klijent kao parametar * treba prihvatiti ime racunala umjesto IP adrese. */ /* * Dodao sam i IPv6 podrsku za UDP echo (uz standardni IPv4). Program se * nalazi kompletan u jednoj datoteci, s time da iz argv[0] saznaje da li * je klijent ili posluzitelj * -kre */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define POSLUZITELJ "./posluzitelj" /* ime posluzitelja */ #define KLIJENT "./klijent" /* ime klijenta */ #define KRAJ "kraj\n" /* kontrolna rijec za kraj transmisije */ #define MAXLINE 4096 /* velicina UDP receive buffera */ #ifndef SOMAXCONN # define SOMAXCONN 25 #endif /* polje error poruka za printhelp() */ static const char *help[] = { "Posluzitelj se poziva sa:\n./posluzitelj \n" /* 0 */ "pri cemu je port cijeli broj 1024:65535.\n", "Klijent se poziva sa:\n./klijent \n" /* 1 */ "pri cemu je IP adresa posluzitelja,\n" "a port cijeli broj 1024:65535.\n", "Nije pozvan niti klijent niti posluzitelj.\n" /* 2 */ "Ispravna sintaksa je:\n" "./posluzitelj ili ./klijent .\n", "Drugi labos iz Mreza racunala, " __DATE__ "\n" /* 3 */ }; /* ispisivanje samih error poruka prema internim kodovima */ int printhelp(int helpno) { return fprintf(stderr, "%s", help[helpno]); } /* myerrorhandler() -> evolucija perror() i exit() u jednu funkciju sa * vracanjem odgovarajucih error poruka sistemu */ void merror(char *errmsg, int lineno) { fprintf(stderr, "Greska u liniji %d: ", lineno); perror(errmsg); exit(errno); } /* kada dijete izadje zbog kljucne rijeci, izadji i iz roditelja */ void dosigchld(int signal) { fputs("Izlazim zbog kljucne rijeci\n", stderr); exit(EXIT_SUCCESS); } /* petlja koja radi mrezni setup te slusa i radi dg_echo() nazad * posiljatelju, ma tko god on bio :-) */ void dolisten(const char *strport) { #ifndef IPV6 struct sockaddr_in recv_addr, sender_addr; int len = sizeof(struct sockaddr_in); #else struct sockaddr_in6 recv_addr, sender_addr; int len = sizeof(struct sockaddr_in6); #endif /* IPV6 */ int port = 0, socketfd, acceptfd = 0, rc = 0; char buf[MAXLINE]; int len2 = sizeof(buf), len3 = sizeof(KRAJ); int optspacer = 1; pid_t pid = 0; /* trazimo integer port iz stringa */ port = atoi(strport); memset(&recv_addr, 0, len); /* obrisi sve */ /* napunimo recv_addr strukturu zeljenim podacima */ #ifndef IPV6 recv_addr.sin_family = AF_INET; /* AF_INET */ recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* svi interfaceovi */ recv_addr.sin_port = htons(port); /* i na kojem portu */ #else recv_addr.sin6_family = AF_INET6; /* AF_INET6 */ recv_addr.sin6_addr = in6addr_any; /* svi interfaceovi */ recv_addr.sin6_port = htons(port); /* i na kojem portu */ #endif /* IPV6 */ /* otvorimo socket ako je moguce, i to za tcp/6 protokol */ #ifndef IPV6 if ((socketfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) merror("Greska u otvaranju socketa", __LINE__); #else if ((socketfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) merror("Greska u otvaranju socketa", __LINE__); #endif /* IPV6 */ /* ucini socket reusable, korisno zbog nasilnog exit() */ setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, (char *)&optspacer, sizeof(optspacer)); /* povezivanje imena i socketa */ if (bind(socketfd, (struct sockaddr*)&recv_addr, len) == -1) merror("Greska u povezivanju imena i socketa", __LINE__); /* postavi listener */ if (listen(socketfd, SOMAXCONN) == -1) merror("Greska u slusanju", __LINE__); /* jedna mala beskonacna petlja - iskreno, ja bih radije jos cekao * podatke sa socketa pomocu select() ili poll() petlje, no kako nam je * socket blocking tipa, nema potrebe */ for (;;) { if ((acceptfd = accept(socketfd, (struct sockaddr*)&sender_addr, &len)) == -1) merror("Greska u primanju paketa", __LINE__); signal(SIGCHLD, dosigchld); if ((pid = fork()) == -1) merror("Greska u fork pozivu", __LINE__); /* roditelj */ if (pid) continue; else pid = getpid(); /* dijete */ for (;;) { memset(buf, 0, len2); if ((rc = read(acceptfd, buf, len2)) <= 0) merror("Greska u citanju iz socketa", __LINE__); fprintf(stdout, "[%u] Primio sam> %s", pid, buf); if (write(acceptfd, buf, rc) <= 0) merror("Greska u pisanju u socket", __LINE__); if (!strncmp(buf, KRAJ, len3)) { fprintf(stdout, "[%u] Izlazim...\n", pid); close(acceptfd); exit(EXIT_SUCCESS); } } } } void doconnect(const char *straddr, const char *strport) { struct hostent *hp = NULL; #ifndef IPV6 struct in_addr ip; struct sockaddr_in server_addr; int len = sizeof(struct sockaddr_in); #else struct in6_addr ip; struct sockaddr_in6 server_addr; int len = sizeof(struct sockaddr_in6); #endif /* IPV6 */ char buf[MAXLINE]; int socketfd, rc = 0, port = 0; int len2 = sizeof(buf), len3 = sizeof(KRAJ); #ifndef IPV6 /* pokusaj dobiti binary adresu iz stringa */ #ifndef SOLARIS if (inet_aton(straddr, &ip) == 0) #else if ((ip.s_addr = inet_addr(straddr)) == -1) #endif { /* ako to ne ide, pokusaj ga dobiti iz imena */ hp = gethostbyname(straddr); if (hp != NULL) memcpy(&ip.s_addr, hp->h_addr_list[0], hp->h_length); else merror("Nepoznata adresa", __LINE__); } #else if (inet_pton(AF_INET6, straddr, &ip) <= 0) { hp = gethostbyname2(straddr, AF_INET6); if (hp != NULL) memcpy(&ip, hp->h_addr_list[0], sizeof(ip.s6_addr)); else merror("Nepoznata adresa", __LINE__); } #endif /* IPV6 */ /* trazimo integer port iz stringa */ port = atoi(strport); memset(&server_addr, 0, len); /* obrisi sve */ #ifndef IPV6 /* napunimo recv_addr strukturu zeljenim podacima */ server_addr.sin_family = AF_INET; /* AF_INET */ server_addr.sin_addr.s_addr = ip.s_addr; /* salji udaljenom stroju */ server_addr.sin_port = htons(port); /* i na kojem portu */ #else server_addr.sin6_family = AF_INET6; /* AF_INET */ server_addr.sin6_addr = ip; /* salji udaljenom stroju */ server_addr.sin6_port = htons(port); /* i na kojem portu */ #endif /* IPV6 */ /* otvorimo socket ako je moguce, i to za tcp/6 protokol */ #ifndef IPV6 if ((socketfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) merror("Greska u otvaranju socketa", __LINE__); #else if ((socketfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) merror("Greska u otvaranju socketa", __LINE__); #endif /* IPV6 */ if (connect(socketfd, (struct sockaddr*)&server_addr, len) == -1) merror("Ne mogu se spojiti na posluzitelj", __LINE__); for (;;) { fputs("Unesi niz> ", stdout); fgets(buf, len2, stdin); rc = strlen(buf); /* okej, a sad posalji sve sto si primio */ if (write(socketfd, buf, rc) <= 0) merror("Ne mogu pisati u socket", __LINE__); memset(buf, 0, len2); if (read(socketfd, buf, len2) <= 0) merror("Ne mogu citati iz socketa", __LINE__); fprintf(stdout, "Dobiveno nazad> %s", buf); if (!strncmp(buf, KRAJ, len3)) break; } close(socketfd); } int main(int argc, char **argv) { /*intro tekst */ printhelp(3); /* provjeri da li je pozvan posluzitelj.. */ if (!strncmp(argv[0], POSLUZITELJ, strlen(KLIJENT))) (argc != 2) ? printhelp(0) : dolisten(argv[1]); else /* ..ili klijent */ if (!strncmp(argv[0], KLIJENT, strlen(KLIJENT))) (argc != 3) ? printhelp(1) : doconnect(argv[1], argv[2]); else /* ili je greska! */ printhelp(2), exit(EXIT_FAILURE); return EXIT_SUCCESS; }