Um retorno de chamada em C é uma função fornecida a outra função para "retornar a chamada" em algum momento em que a outra função está executando sua tarefa.
Há duas maneiras pelas quais um retorno de chamada é usado : retorno de chamada síncrono e retorno de chamada assíncrono. Um retorno de chamada síncrono é fornecido para outra função que realiza alguma tarefa e depois retorna ao chamador com a tarefa concluída. Um retorno de chamada assíncrono é fornecido para outra função que iniciará uma tarefa e retornará ao chamador com a tarefa possivelmente não concluída.
Um retorno de chamada síncrono é normalmente usado para fornecer um delegado para outra função na qual a outra função delega alguma etapa da tarefa. Exemplos clássicos dessa delegação são as funções bsearch()e qsort()da Biblioteca Padrão C. Ambas as funções recebem um retorno de chamada que é usado durante a tarefa que a função está fornecendo, para que o tipo de dados pesquisado, no caso de bsearch(), ou classificado, no caso de qsort(), não precise ser conhecido pela função que está sendo usava.
Por exemplo, aqui está um pequeno programa de amostra com o bsearch()uso de diferentes funções de comparação, retornos de chamada síncronos. Ao nos permitir delegar a comparação de dados em uma função de retorno de chamada, a bsearch()função nos permite decidir em tempo de execução que tipo de comparação queremos usar. Isso é síncrono porque, quando a bsearch()função retorna, a tarefa é concluída.
#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);
}
}
Um retorno de chamada assíncrono é diferente, pois quando a função chamada à qual fornecemos um retorno de chamada retorna, a tarefa pode não ser concluída. Esse tipo de retorno de chamada é frequentemente usado com E / S assíncrona na qual uma operação de E / S é iniciada e, quando concluída, o retorno de chamada é invocado.
No programa a seguir, criamos um soquete para escutar solicitações de conexão TCP e, quando uma solicitação é recebida, a função que escuta chama a função de retorno de chamada fornecida. Esse aplicativo simples pode ser exercitado executando-o em uma janela enquanto você usa o telnetutilitário ou um navegador da Web para tentar se conectar em outra janela.
Tirei a maior parte do código WinSock do exemplo que a Microsoft fornece com a accept()função em https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
Esse aplicativo inicia um listen()host local, 127.0.0.1, usando a porta 8282, para que você possa usar um telnet 127.0.0.1 8282ou outro http://127.0.0.1:8282/.
Este aplicativo de exemplo foi criado como um aplicativo de console com o Visual Studio 2017 Community Edition e está usando a versão dos soquetes do Microsoft WinSock. Para um aplicativo Linux, as funções WinSock precisariam ser substituídas pelas alternativas Linux e a biblioteca de threads do Windows usaria pthreads.
#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);
}