Un rappel en C est une fonction qui est fournie à une autre fonction pour "rappeler" à un moment donné lorsque l'autre fonction exécute sa tâche.
Il y a deux façons d'utiliser un rappel : le rappel synchrone et le rappel asynchrone. Un rappel synchrone est fourni à une autre fonction qui va effectuer une tâche puis revenir à l'appelant avec la tâche terminée. Un rappel asynchrone est fourni à une autre fonction qui va démarrer une tâche puis revenir à l'appelant avec la tâche éventuellement non terminée.
Un rappel synchrone est généralement utilisé pour fournir un délégué à une autre fonction à laquelle l'autre fonction délègue une étape de la tâche. Des exemples classiques de cette délégation sont les fonctions bsearch()
et qsort()
de la bibliothèque standard C. Ces deux fonctions prennent un rappel qui est utilisé pendant la tâche que la fonction fournit afin que le type des données recherchées, dans le cas de bsearch()
, ou triées, dans le cas de qsort()
, n'ait pas besoin d'être connu par la fonction étant utilisé.
Par exemple, voici un petit exemple de programme bsearch()
utilisant différentes fonctions de comparaison, des rappels synchrones. En nous permettant de déléguer la comparaison des données à une fonction de rappel, la bsearch()
fonction nous permet de décider au moment de l'exécution quel type de comparaison nous voulons utiliser. Ceci est synchrone car lorsque la bsearch()
fonction retourne, la tâche est terminée.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int iValue;
int kValue;
char label[6];
} MyData;
int cmpMyData_iValue (MyData *item1, MyData *item2)
{
if (item1->iValue < item2->iValue) return -1;
if (item1->iValue > item2->iValue) return 1;
return 0;
}
int cmpMyData_kValue (MyData *item1, MyData *item2)
{
if (item1->kValue < item2->kValue) return -1;
if (item1->kValue > item2->kValue) return 1;
return 0;
}
int cmpMyData_label (MyData *item1, MyData *item2)
{
return strcmp (item1->label, item2->label);
}
void bsearch_results (MyData *srch, MyData *found)
{
if (found) {
printf ("found - iValue = %d, kValue = %d, label = %s\n", found->iValue, found->kValue, found->label);
} else {
printf ("item not found, iValue = %d, kValue = %d, label = %s\n", srch->iValue, srch->kValue, srch->label);
}
}
int main ()
{
MyData dataList[256] = {0};
{
int i;
for (i = 0; i < 20; i++) {
dataList[i].iValue = i + 100;
dataList[i].kValue = i + 1000;
sprintf (dataList[i].label, "%2.2d", i + 10);
}
}
// ... some code then we do a search
{
MyData srchItem = { 105, 1018, "13"};
MyData *foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_iValue );
bsearch_results (&srchItem, foundItem);
foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_kValue );
bsearch_results (&srchItem, foundItem);
foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_label );
bsearch_results (&srchItem, foundItem);
}
}
Un rappel asynchrone est différent en ce que lorsque la fonction appelée à laquelle nous fournissons un rappel revient, la tâche peut ne pas être terminée. Ce type de rappel est souvent utilisé avec les E / S asynchrones dans lesquelles une opération d'E / S est lancée, puis, une fois terminée, le rappel est appelé.
Dans le programme suivant, nous créons une socket pour écouter les demandes de connexion TCP et lorsqu'une demande est reçue, la fonction qui fait l'écoute appelle alors la fonction de rappel fournie. Cette application simple peut être exercée en l'exécutant dans une fenêtre tout en utilisant l' telnet
utilitaire ou un navigateur Web pour tenter de se connecter dans une autre fenêtre.
J'ai extrait la plupart du code WinSock de l'exemple fourni par Microsoft avec la accept()
fonction à l' adresse https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
Cette application démarre un listen()
sur l'hôte local, 127.0.0.1, en utilisant le port 8282 afin que vous puissiez utiliser soit telnet 127.0.0.1 8282
ou http://127.0.0.1:8282/
.
Cet exemple d'application a été créé en tant qu'application console avec Visual Studio 2017 Community Edition et utilise la version Microsoft WinSock des sockets. Pour une application Linux, les fonctions WinSock devraient être remplacées par les alternatives Linux et la bibliothèque de threads Windows utiliserait à la pthreads
place.
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
// function for the thread we are going to start up with _beginthreadex().
// this function/thread will create a listen server waiting for a TCP
// connection request to come into the designated port.
// _stdcall modifier required by _beginthreadex().
int _stdcall ioThread(void (*pOutput)())
{
//----------------------
// Initialize Winsock.
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed with error: %ld\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for listening for
// incoming connection requests.
SOCKET ListenSocket;
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound.
struct sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(8282);
if (bind(ListenSocket, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR) {
printf("bind failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//----------------------
// Listen for incoming connection requests.
// on the created socket
if (listen(ListenSocket, 1) == SOCKET_ERROR) {
printf("listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//----------------------
// Create a SOCKET for accepting incoming requests.
SOCKET AcceptSocket;
printf("Waiting for client to connect...\n");
//----------------------
// Accept the connection.
AcceptSocket = accept(ListenSocket, NULL, NULL);
if (AcceptSocket == INVALID_SOCKET) {
printf("accept failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else
pOutput (); // we have a connection request so do the callback
// No longer need server socket
closesocket(ListenSocket);
WSACleanup();
return 0;
}
// our callback which is invoked whenever a connection is made.
void printOut(void)
{
printf("connection received.\n");
}
#include <process.h>
int main()
{
// start up our listen server and provide a callback
_beginthreadex(NULL, 0, ioThread, printOut, 0, NULL);
// do other things while waiting for a connection. In this case
// just sleep for a while.
Sleep(30000);
}