#include <pthread.h>
//include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
//include <string.h>
#include <unistd.h>
//include <errno.h>
#include <ctype.h>

#define SOCKET_ERR      1
#define BIND_ERR        2
#define LISTEN_ERR      3
#define ACCEPT_ERR      4
#define TH_CREATE_ERR   5
#define RECV_ERR        6
#define SEND_ERR        7       
#define CLOSE_SOCK_ERR  8
#define GETHOSTN_ERR    9
#define CONNECT_ERR     10
#define SOCKET_REUSEADDR_ERR 11

#define BUF_SIZE    1500
#define PORT        4404

/*============================================================================*/
// Client HTTP donc le serveur répond par un en-tête HTTP suivi du code HTML
// sinon le client ne comprendra pas ce qui se passe
void envoi(int sk, const char* s, int len){
    char paquet[100];
    len = sprintf(paquet,
            "HTTP/1.1 200 OK\n"
            "Content-Length:%d\n"
            "Access-Control-Allow-Origin:*\n"
            "Content-type:text/html\r\n"
            "\r\n"
            "%s", len, s);
    if(send(sk, paquet, len, 0) == -1) print_error(SEND_ERR, errno);
}
/*============================================================================*/
// et si on n'envoyait que le canal rouge?
void envoiUneImage(int sk){
    printf("\tenvoi image\n");
    char headerHTTP[100];
    u8 lenH;
    u32 sent = 0;

    //len = CommonV4l2_get_image_size(&common_v4l2);
    lenH = sprintf(headerHTTP,
                    "HTTP/1.1 200 OK\n"
                    "Content-Length:%d\n"
                    //"Content-Type:image/x-portable-graymap\n"
                    "Content-Type:image/x-portable-pixmap",
                    "Access-Control-Allow-Origin:*\n" // sinon ajax de plaint de ce truc
                    "\r\n", lastframe.len);

    sent = send(sk, headerHTTP, lenH, 0);
    if(sent == -1) print_error(SEND_ERR, errno);
    //printf(" header %d/%d octets envoyés, ", lenH, sent);

    //sent = send(sk, CommonV4l2_get_image(&common_v4l2), CommonV4l2_get_image_size(&common_v4l2), 0);
    sent = send(sk, lastframe.pixels, lastframe.len, 0);
    //printf(" data %d/%d octets envoyés\n", lastframe.len, sent);
    if(sent == -1) print_error(SEND_ERR, errno);
}
/*============================================================================*/
// crée un socket listen puis accepte un client, lit et écrit
	SC1 commencer le scan
	SC0 stop le scan
	200    tourner à gauche (moteur 1)
	210    tourner à droite
	220    tourner en haut (moteur 2)
	230    tourner en bas
	300    Filmer
	909 Fermer le serveur

void* threadReseau()
{
    char buf[BUF_SIZE];
    int len, i, sk, sks;
    struct sockaddr_in th_sock_addr;
    int th_addr_size;

    printf("Thread réseau\n");

    //sks = lancerServeur(); // socket serveur
    struct sockaddr_in sAddr;

    if((sks = socket(AF_INET, SOCK_STREAM, 0)) == -1) print_error(SOCKET_ERR, errno);

    // en console CTRL-C pour stopper le serveur sinon bind() dit adress already in use
    // pour ne pas être forcé de faire CTRL-C, lui précise reuseaddr 
    if (setsockopt(sks, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) print_error(SOCKET_REUSEADDR_ERR, errno);

    bzero(&sAddr, sizeof(sAddr));
    sAddr.sin_family = AF_INET;
    sAddr.sin_port = htons(PORT);
    sAddr.sin_addr.s_addr = INADDR_ANY; // allow connections from any address

    //printf("\tbind() associate socket to specified port in sAddr\n");
    if(bind(sks, (struct sockaddr*)&sAddr, sizeof(sAddr)) == -1) print_error(BIND_ERR, errno);

    //printf("\tlisten() listen makes a queue of 1 client request\n");
    if((listen(sks, 1)) == -1) print_error(LISTEN_ERR, errno);

    // socket client
    sk = socket(AF_INET, SOCK_STREAM, 0);
    if(sk == -1) print_error(SOCKET_ERR, errno);

    // Attend qu'un client se connecte, à partir de là
    // il reçoit les codes du client, exécute et lui envoie une confirmation
    // si la connection est perdue il se remet en attente de client
    while(1)
    {
        printf("Waiting for client ...\n");
        sk = accept(sks, (struct sockaddr*)&th_sock_addr, (int*)&th_addr_size);
        if(sk == -1) print_error(ACCEPT_ERR, errno);
        printf("\tNew client accepted\n");

        while(1){
            len = recv(sk, buf, BUF_SIZE, 0);
            if(len == -1) print_error(RECV_ERR, errno);
            if(len == 0) break; // connection perdue passe à l'acceptation suivante
            //buf[len]=0;
            /*printf("Message from client [%s]\n", buf);
            Le client web envoie une requête HTTP
                GET /220 HTTP/1.1
                Host: 127.0.0.1:4404
                Connection: keep-alive
                User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
                Accept: * / *
                Origin: null
                Sec-Fetch-Site: cross-site
                Sec-Fetch-Mode: cors
                Sec-Fetch-Dest: empty
                Accept-Encoding: gzip, deflate, br
                Accept-Language: fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7 suivi de 2 lignes vides

                inutile de parser la requête HTTP: le code commence au 5e octet
                donc regarde rapidement les 5e, 6e et 7e octets
            */
            if(buf[5]=='S' && buf[6]=='C'){
                if(buf[7]=='1') {
                    printf("\tSC1 je passe en mode SCAN\n");
                    mode = SCAN; // quand le thread tourelle verra ça il commencera à bouger
                    //printf("init webcam\n");
                    CommonV4l2_init(&common_v4l2, "/dev/video0", x_res, y_res);
                    envoi(sk, "SC1",3); // confirmation au client que l'ordre va être exécuté
                }
                else if(buf[7]=='0') {
                    printf("\tSC0 je stoppe le mode SCAN\n");
                    mode = IDLE;
                    CommonV4l2_deinit(&common_v4l2); //éteint la webcam pour économiser la batterie
                    envoi(sk, "SC0",3); // confirmation au client que l'ordre va être exécuté
                }
                // Ordre de déplacement SC2
                if     (buf[7]=='2') {AllerAGauche(10); envoi(sk, "okG",3);}
                else if(buf[7]=='3') {AllerADroite(10); envoi(sk, "okD",3);}
                else if(buf[7]=='4') {AllerEnHaut(10);  envoi(sk, "okH",3);}
                else if(buf[7]=='5') {AllerEnBas(10);   envoi(sk, "okB",3);}
            }
            else if(buf[5]=='I'){
                if (buf[6]=='M' && buf[7]=='G'){
                    //printf("\tIMG Le client web veut une preview de la webcam\n");
                    // envoie la réponse HTTP header + buffer array sans header PPM
                    /*if(mode == IDLE){
                        CommonV4l2_init(&common_v4l2, "/dev/video0", x_res, y_res);
                        grabFrameFromCamera();
                        envoiUneImage(sk);
                        CommonV4l2_deinit(&common_v4l2);
                    }
                    else*/ envoiUneImage(sk);
                }
                // Filmer avec le Nikon
                else if(buf[6]=='N' && buf[7]=='K'){
                    printf("\tIMG actionne le servomoteur qui appuie sur la télécommande bluetooth du Nikon\n");
                    envoi(sk, "Nik",3);
                    ServoAppuyerSurBoutonBluetooth();
                }
            }
            // le client envoie un code 909 il veut tomber l'exe serveur
            else if(buf[5]=='9' && buf[6]=='0' && buf[7]=='0'){
                envoi(sk, "bye",3);
                fermer = 1; // quand le thread tourelle verra ça il sortira
                break; // je sors de cette communication ...
            }
        }

        if(fermer==1) { // ... et s'il faut terminer l'appli on ferme
            printf("\tOn ferme\n");
            break;
        }
        printf("\tje passe à l'acceptation suivante\n");
    }

    printf("\tClose sk\n");    
    if(close(sk) == -1) print_error(CLOSE_SOCK_ERR, errno);
    printf("\tClose sks\n");
    if(close(sks) == -1) print_error(CLOSE_SOCK_ERR, errno);
}
reseau.h 191 lignes, 6935 octets