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 telnet
utilitá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 8282
ou 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);
}