Prenoenje 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() - slua 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 zaguivala LAN vezu između njih.
Slika na web stranici je smjetena 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 naem 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;
}
|
|