Les sockets sont un outil puissant en programmation C afin d’échanger des données entre différentes application. Les socket peuvent être utilisé avec différents langage de programmation. Dans ce tutoriel, nous allons créer deux programmes qui communique à travers d’un socket.
Principe
On utilise une socket TCP pour établir une connexion entre deux processus écrit en langage C.
- Le serveur (ici
device.c
) écoute sur un port (ex. 12345). - Le client (ici
commande.c
) se connecte à ce port et envoie des commandes.
Dans le progamme device.c, on simule la réception de booléen de flottant et d’entier et une fonction d’activation. Dans le programme commande, on simule la stimulation de ces entrées.
Code serveur
Nous allons mettre en place un serveur socket sur le port 12345, puis nous allons préparer la réception des commandes provenant du ou des clients.
// device.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #ifdef _WIN32 #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #else #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #endif // Etat du device typedef struct { int discrete; // 0=off, 1=on float analog; // valeur analogique short num; // entier 16 bits int led; // 0=off, 1=on } DeviceState; void send_response(int client, const char *msg) { send(client, msg, strlen(msg), 0); } int main() { #ifdef _WIN32 WSADATA wsa; WSAStartup(MAKEWORD(2,2),&wsa); #endif int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv = {0}; serv.sin_family = AF_INET; serv.sin_port = htons(12345); serv.sin_addr.s_addr = INADDR_ANY; bind(sockfd,(struct sockaddr*)&serv,sizeof(serv)); listen(sockfd,1); printf("[DEVICE] En attente de connexion...\n"); int client = accept(sockfd,NULL,NULL); printf("[DEVICE] Client connecte.\n"); DeviceState state = {0,0.0,0,0}; char buf[256]; while(1) { int n = recv(client, buf, sizeof(buf)-1, 0); if(n<=0) break; buf[n]=0; buf[strcspn(buf,"\r\n")] = 0; // suppression retour ligne if(strncmp(buf,"SET_DISCRETE on",15)==0) { state.discrete=1; printf("[MCU] Discrete on\n"); send_response(client,"OK\n"); } else if(strncmp(buf,"SET_DISCRETE off",16)==0) { state.discrete=0; printf("[MCU] Discrete off\n"); send_response(client,"OK\n"); } else if(strncmp(buf,"SET_ANALOG",10)==0) { state.analog = atof(buf+11); printf("[MCU] Analog %f\n",state.analog); send_response(client,"OK\n"); } else if(strncmp(buf,"SET_NUM",7)==0) { state.num = (short)atoi(buf+8); printf("[MCU] Integer %d\n",state.num); send_response(client,"OK\n"); } else if(strncmp(buf,"LED on",6)==0) { state.led=1; send_response(client,"LED ON\n"); } else if(strncmp(buf,"LED off",7)==0) { state.led=0; send_response(client,"LED OFF\n"); }else if(strncmp(buf,"STATUS",6)==0) { time_t t = time(NULL); struct tm *tm_info = localtime(&t); char timestr[64]; strftime(timestr, sizeof(timestr), "%d/%m/%Y %H:%M:%S", tm_info); char resp[256]; snprintf(resp,sizeof(resp), "TIME=%s DISCRETE=%d ANALOG=%.2f NUM=%d LED=%d\n", timestr, state.discrete, state.analog, state.num, state.led); send_response(client,resp); } else if(strncmp(buf,"QUIT",4)==0) { send_response(client,"BYE\n"); break; } else { send_response(client,"CMD UNKNOWN\n"); } } #ifdef _WIN32 closesocket(client); closesocket(sockfd); WSACleanup(); #else close(client); close(sockfd); #endif printf("[DEVICE] Arret.\n"); return 0; }
Code client
Dans le code client, nous allons nous connecter au serveur et définir différentes commandes afin d’interagir avec le code device. Le programme va envoyer les commandes taper dans le terminal puis va les envoyer via un flux de donnée. Il affichera dans un second temps les réponses provenant du programme device.
// commande.c #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef _WIN32 #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #else #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #endif int recv_line(int sock, char *buf, int maxlen) { int i=0; char c; while(i<maxlen-1) { int n=recv(sock,&c,1,0); if(n<=0) return n; buf[i++]=c; if(c=='\n') break; } buf[i]=0; return i; } int main() { #ifdef _WIN32 WSADATA wsa; WSAStartup(MAKEWORD(2,2),&wsa); #endif int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv = {0}; serv.sin_family = AF_INET; serv.sin_port = htons(12345); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sockfd,(struct sockaddr*)&serv,sizeof(serv))<0) { perror("Connexion echouee"); return 1; } printf("[CMD] Connecte au device.\n"); char cmd[128], buf[256]; while(1) { printf("Commande (SET_DISCRETE on/off, SET_ANALOG x, SET_NUM x, LED on/off, STATUS, QUIT): "); if(!fgets(cmd,sizeof(cmd),stdin)) break; if(strncmp(cmd,"QUIT",4)==0) { send(sockfd,cmd,strlen(cmd),0); recv_line(sockfd,buf,sizeof(buf)); printf("[CMD] Recv: %s",buf); break; } send(sockfd,cmd,strlen(cmd),0); int n=recv_line(sockfd,buf,sizeof(buf)); if(n>0) printf("[CMD] Recv: %s",buf); } #ifdef _WIN32 closesocket(sockfd); WSACleanup(); #else close(sockfd); #endif return 0; }
Compilation et test
Les commandes de compilation pour les différentes machines sont sensiblement les mêmes si ce n’est que sur linux la librairie socket existe par défaut.
gcc device.c -o device gcc commande.c -o commande
Compilation pour windows
gcc device.c -o device.exe -lws2_32 gcc commande.c -o commande.exe -lws2_32
Lancer d’abord device.exe dans un premier terminal, puis commande.exe dans un second. Vous pouvez ensuite tester les commandes définies et observer la communication via socket entre les deux programmes C.
L’outil socket permettent de communiquer entre programme de différents langage comme C et Python notamment via Wifi.
Sources