Zammoda VS 2004 Zammoda VS 2004
Projektni zadatak Kamera Motor Klijent - poslužitelj Web design Download

Prenošenje slike i upravljanje motorom preko mreže realizirano je tzv. klijent-poslužitelj modelom. Komunikacija je ostvarena nizom zahtjeva i odgovora; klijent šalje zahtjev poslužitelju, a poslužitelj obrađuje zahtjev i šalje odogovor klijentu. Klijent i poslužitelj moraju govoriti istom jezikom tj. koristiti isti protokol.


Krajnja točka komunikacije između klijentskog procesa i poslužiteljskog procesa je priključnica (socket). To je programska apstrakcija koja omogućuje rad s mrežom. Priključnica je definirana transportnim protokolom (npr. TCP), IP adresom (npr. 161.53.64.29) i brojem vrata (port - identifikacijska oznaka pojedine usluge koju pruža poslužitelj).


Na slici se vidi način realizacije TCP poslužitelja i klijenta :


U nastavku će kratko biti opisani sistemski pozivi vezani za TCP priključnice:

  • socket() - stvara novi socket
  • bind() - povezuje socket s IP adresom i brojem vrata
  • listen() - sluša na definiranim vratima (portu) zahtjeve za uspostavom veze
  • accept() - prihvaća vezu te kreira kopiju socketa; "stari" socket vodi brigu o posluživanju novih zahtjeva, dok novi obavlja posao
  • send()/recv() - slanje/primanje podataka

Na procesnom računalu nalaze se dva poslužitelja. Jedan se bavi prijenosom slike dok je drugi zadužen za praćenje položaja motora i upravljanje njime.

Oba poslužitelja rade stalno i prvi se trebaju pokrenuti!



Kanal za prijenos slike

Na poslužiteljskom računalu nalazi se početna web stranica sustava. Kada korisnik želi pristupiti sustavu, on samo mora učitati tu web stranicu nekim od postojeŠih web preglednika.

Kada poslužiteljsko računalo dobije zahtjev za web stranicom od korisnika, ono kontaktira procesno računalo radi dohvata najnovijeg prikaza kamere. Procesno računalo tada pokreŠe program "streamer" koji prikaz s kamere sprema kao sliku u JPEG formatu. Nakon toga, procesno računalo šalje sliku poslužitelju (preko socket mehanizma), a poslužitelj tu sliku prosljeđuje korisniku zajedno s ostatkom web stranice. Veze između poslužiteljskog i procesnog racunala je iskljucivo "on demand" kako ne bi trajno zagušivala LAN vezu između njih.

Slika na web stranici je smještena unutar zasebnog okvira (frame). Osvježavanje prikaza slike na strani korisnika izvedeno je automatskim učitavanjem tog okvira stranice nakon određenog vremena.

NAPOMENA: Da bi i korisnici Microsoft Internet Explorera mogli primati uvijek svježi prikaz slike, potrebno je u tom pregledniku postaviti opciju da se stranica ne učitava iz pričuvne memorije na disku (cache). To se postavlja u izborniku "Tools > Internet Options". U novootvorenom dijalog prozoru potrebno je u odjeljku "Temporary Internet Files" kliknuti na tipku "Settings". Tada Še se otvoriti novi dijalog prozor u kojem treba postaviti opciju "Check for newer version of stored pages:" na vrijednost "Every visit to the page".



Kamera poslužitelj


Prevodi se s :
gcc cserver.c -o cserver.exe

Pokreće se s :
./cserver.exe

** cserver.c - a stream socket server
** taken from "Beej's Guide to Network Programming"
** Works with STREAM_MACHINE.C
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <signal.h>

#define BUFSIZE 4096
#define PORT 3490 // the port users will be connecting to
#define BACKLOG 1 // how many pending connections queue will hold
#define FILENAME "tosend.jpg"

void sigchld_handler(int s) {
while(wait(NULL) > 0);
}
int main(void) {
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
int sin_size;
struct sigaction sa;
int yes=1;

int fp;
char buf[BUFSIZE];
int n,s;


	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	perror("socket");
	exit(1);
	}

	if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
	perror("setsockopt");
	exit(1);
	}

my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct

	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(1);
	}
	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	} 
//a signal-handler for cleaning up all terminated child-processes
sa.sa_handler = sigchld_handler; 
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;

	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}
	while(1) { // main accept() loop
		sin_size = sizeof(struct sockaddr_in);
			if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1) {
				perror("accept");
				continue;
			}
		printf("server: got connection from %s\n",
		inet_ntoa(their_addr.sin_addr));

			if (!fork()) { // this is the child process
				close(sockfd); // child doesn't need the listener
				fp=open(FILENAME,O_RDONLY);
				if(fp==-1) 
					perror("open failed");
				//reading from a file and sending data to socket
				while((n=read(fp,buf,BUFSIZE))>0) {
					s=send(new_fd,buf,n, 0);
					if(s==0) 
						perror("send");
					printf("Procitano %d podataka\tPoslano %d podataka\n",n,s);
				}
				close(fp);
				close(new_fd);
				exit(0);
			}
		close(new_fd); // parent doesn't need this
	}
return 0;
}
  

Nohup naredbom omogucujemo izvodjenje programa i nakon odjave korisnika koji je poikrenuo skriptu sa sustava. Njome se zapravo svim aktivnim procesima korisnika uskracuje signal SIGHUP prilikom odjave iz sustava. Navedeno kreiranje soft linka nohup.out na null datoteku je potrebno jer nohup naredba preusmjerava izlazne poruke pokrenutih programa (servera + stream machine) u nohup.out datoteku koja s vremenom vrlo brzo raste.


Naravno uz njega ide cclient.c


Prevodi se s :
gcc cclient.c -o cclient.exe

Pokreće se s :
./cclient.exe

/*
** cclient.c - a stream socket client
** "core" taken from "Beej's Guide to Network Programming"
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/fcntl.h>

#define PORT 3490 // the port client will be connecting to
#define MAXDATASIZE 4096+1 // max number of bytes we can get at once
#define BUFFER "buf.jpg"
#define LOCALFILE "img.jpg"
#define SERVER "161.53.64.29"

int main(int argc, char *argv[])
{
static int total;
int sockfd, numbytes;
int fpp;

char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in their_addr; // connector's address information

//while(1) {

	if ((he=gethostbyname(SERVER)) == NULL) { // get the host info
		perror("gethostbyname");
		exit(1);
	}
	
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}


	their_addr.sin_family = AF_INET; // host byte order
	their_addr.sin_port = htons(PORT); // short, network byte order
	their_addr.sin_addr = *((struct in_addr *)he->h_addr);

	memset(&(their_addr.sin_zero), '\0', 8); // zero the rest of the struct

		

	if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
				perror("connect");
				exit(1);
				}
	//file for receving image
	if((fpp = open(BUFFER,O_CREAT|O_TRUNC|O_WRONLY))==-1)
			perror("open failed");
	//reading from socket and writing to a file
	while((numbytes=recv(sockfd,(char *) buf, MAXDATASIZE-1, 0))>0)  {
			printf("primljeno %d podataka\n",numbytes);
			write(fpp,buf,numbytes);
			total+=numbytes;
			}
		
	if(numbytes==-1) {
			perror("recv");
			exit(1);
			}
		
	printf("Received ukupno: %ld\n",total);
	close(fpp);
	close(sockfd);

	//copy buffer data to file LOCALFILE
	system("cp buf.jpg LOCALFILE");
	//}
	return 0;
	}



Kanal za prijenos naredbi pomaka motora


Kad korisnik na web stranici klikne "Turn left" ili "Turn right", pokreće se PHP klijent koji motor poslužitelju na procesnom računalu prenese pomak. Motor poslužitelj tada računa trenutnu poziciju i pomiče motor. Na kraju vraća trenutnu poziciju PHP klijentu.



Motor poslužitelj


Za prevođenje postoji makefile
ili "ručno" :
gcc -c step.c -o step.o
gcc -c mserver.c -o mserver.o
gcc -o mserver.exe step.o mserver.o

Pokreće se s :
./mserver.exe

/*
** mserver.c - a stream socket server demo
** "core" taken from "Beej's Guide to Network Programming"
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <signal.h>
//steper library
#include "step.h"


#define BUFSIZE 3+1
#define MOT_PORT 3500 // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold

void sigchld_handler(int s)
{
while(wait(NULL) > 0);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
int numbytes,s;
struct sockaddr_in my_addr; // my address information
struct sockaddr_in their_addr; // connector's address information
int sin_size;
struct sigaction sa;
int yes=1;
int fp;
char bufr[1];
char buf[BUFSIZE];
char bufs[BUFSIZE];
int n,i=0;
static char pozicija=0;

	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
	
	if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
		perror("setsockopt");
		exit(1);
	}
	
my_addr.sin_family = AF_INET; // host byte order
my_addr.sin_port = htons(MOT_PORT); // short, network byte order
my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(1);
	}
	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	}	
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}
	while(1) { // main accept() loop
		sin_size = sizeof(struct sockaddr_in);
		if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1) {
			perror("accept");
			continue;
		}
	printf("server: got connection from %s\n",
	inet_ntoa(their_addr.sin_addr));

	// motor rotation (left or right)
	numbytes=recv(new_fd, buf,BUFSIZE,0);
		if(numbytes==-1) {
			perror("recv");
			exit(1);
		}
	buf[3]='\0';	
	printf("PRIMLJENI PODATAK>> %s\n",buf);
	//calculating current position and passing command to motor
	if ( !strcmp(buf,"DES") ) {
		if(pozicija>=10) {
		printf("nedozvoljeni pomak\n");
		}
		else {
		//komanda steperu
		move ('r');
		printf("Primio>> pomak udesno\n");
		//proracun trenutne pozicije
		pozicija++;
		}
		}
	else if( !strcmp(buf,"LIJ") ) {
		if(pozicija<=-10)
		printf("nedozvoljeni pomak\n");
		else {
		//komanda steperu
		move ('l');
		printf("Primio>> pomak ulijevo\n");
		//proracun trenutne pozicije
		pozicija--;
		}
		}
	else if( !strcmp(buf,"NPO") ) printf("New Page Opened!\n");
	else printf("Primio>> nepoznati pomak!!!!!!\n");


//2. sending current position
	bufr[0] = pozicija;
	s=send(new_fd, bufr, sizeof(bufr),0);
	if (s==0) perror ("send");
	printf("Poslao poziciju>> %d\n",(int) bufr[0]);
	close(new_fd);
	}
close(sockfd);
return 0;
}


Razvojna verzija pripadajućeg mu klijenta je mclient.c (mclient u našem sustavu je napisan u PHP-u, ovo je samo demo!).


Prevodi se s :
gcc mclient.c -o mclient.exe

Pokreće se s :
./mclient.exe

/*
** mclient.c - a stream socket client demo
** "core" taken from "Beej's Guide to Network Programming"
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#define PORT 3500 // the port client will be connecting to

#define MAXDATASIZE 3+1 // max number of bytes we can get at once
#define SERVER "161.53.64.29"
int main(int argc, char *argv[])
{
static int total;
int sockfd, numbytes,s;
char buf[MAXDATASIZE]="LIJ";
char bufr[MAXDATASIZE];
int i=0;
static char polozaj=0;
struct hostent *he;
struct sockaddr_in their_addr; // connector's address information
	
		if ((he=gethostbyname(SERVER)) == NULL) { // get the host info
		perror("gethostbyname");
		exit(1);
	}
	
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
their_addr.sin_family = AF_INET; // host byte order
their_addr.sin_port = htons(PORT); // short, network byte order
their_addr.sin_addr = *((struct in_addr *)he->h_addr);

memset(&(their_addr.sin_zero), '\0', 8); // zero the rest of the struct

	if (connect(sockfd, (struct sockaddr *)&their_addr,
		sizeof(struct sockaddr)) == -1) {
		perror("connect");
		exit(1);
	}
//receving current position
	numbytes=recv(sockfd, bufr, MAXDATASIZE-1, 0);
		if(numbytes==-1||numbytes==0) {
			perror("recv");
			exit(1);
		}

	polozaj+=bufr[0];
	printf("trenutni polozaj %d\n",(int) polozaj);
	if ((polozaj<-10) || (polozaj>10)) {
		printf("nedozvoljeni polozaj\n");
		//exit(1);
	}

//sending motor rotation 
	s=send(sockfd,buf,MAXDATASIZE,0); 
	if(s==0)
	perror("send");

printf("poslao %c podatak velicine %d\n",buf[0],sizeof(buf));
		
close(sockfd);
return 0;
}