Una devolución de llamada en C es una función que se proporciona a otra función para "volver a llamar" en algún momento cuando la otra función está haciendo su tarea.
Hay dos formas de utilizar una devolución de llamada : devolución de llamada síncrona y devolución de llamada asíncrona. Se proporciona una devolución de llamada síncrona a otra función que realizará alguna tarea y luego regresará a la persona que llama con la tarea completada. Se proporciona una devolución de llamada asíncrona a otra función que va a iniciar una tarea y luego volver a la persona que llama con la tarea posiblemente no completada.
Una devolución de llamada síncrona se usa generalmente para proporcionar un delegado a otra función a la que la otra función delega algún paso de la tarea. Ejemplos clásicos de esta delegación son las funciones bsearch()
y qsort()
la Biblioteca estándar de C. Ambas funciones toman una devolución de llamada que se utiliza durante la tarea que proporciona la función, de modo que el tipo de los datos que se buscan, en el caso de bsearch()
, u ordenados, en el caso de qsort()
, no necesitan ser conocidos por la función usado.
Por ejemplo, aquí hay un pequeño programa de muestra con el bsearch()
uso de diferentes funciones de comparación, devoluciones de llamada síncronas. Al permitirnos delegar la comparación de datos a una función de devolución de llamada, la bsearch()
función nos permite decidir en tiempo de ejecución qué tipo de comparación queremos usar. Esto es sincrónico porque cuando la bsearch()
función regresa, la tarea está completa.
#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);
}
}
Una devolución de llamada asincrónica es diferente en que cuando la función llamada a la que proporcionamos una devolución de llamada regresa, la tarea puede no completarse. Este tipo de devolución de llamada a menudo se usa con E / S asíncrona en la que se inicia una operación de E / S y luego, cuando se completa, se invoca la devolución de llamada.
En el siguiente programa, creamos un socket para escuchar las solicitudes de conexión TCP y cuando se recibe una solicitud, la función que realiza la escucha invoca la función de devolución de llamada proporcionada. Esta sencilla aplicación puede ejercerse ejecutándola en una ventana mientras usa la telnet
utilidad o un navegador web para intentar conectarse en otra ventana.
Extraje la mayor parte del código WinSock del ejemplo que Microsoft proporciona con la accept()
función en https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
Esta aplicación inicia un listen()
en el host local, 127.0.0.1, utilizando el puerto 8282 para que pueda utilizar telnet 127.0.0.1 8282
o http://127.0.0.1:8282/
.
Esta aplicación de muestra se creó como una aplicación de consola con Visual Studio 2017 Community Edition y está utilizando la versión de sockets Microsoft WinSock. Para una aplicación de Linux, las funciones de WinSock necesitarían ser reemplazadas por las alternativas de Linux y la biblioteca de hilos de Windows usaría pthreads
en su lugar.
#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);
}