Зворотний виклик в C - це функція, яка надається іншій функції, щоб "передзвонити до" в якийсь момент, коли інша функція виконує своє завдання.
Існує два способи використання зворотного дзвінка : синхронний зворотні виклик і асинхронний зворотний виклик. Синхронний зворотний виклик надається іншій функції, яка збирається виконати якусь задачу, а потім повертається до абонента з виконанням завдання. Асинхронний зворотний виклик надається іншій функції, яка збирається запустити завдання, а потім повернутися до абонента, якщо завдання, можливо, не виконане.
Зазвичай синхронний зворотний виклик використовується для надання делегата іншої функції, якій інша функція делегує деякий крок завдання. Класичними прикладами цієї делегації є функції bsearch()
та qsort()
з Бібліотеки стандартних стандартів С. Обидві ці функції приймають зворотний виклик, який використовується під час завдання, яке функція забезпечує таким чином, що тип даних, що шукаються, у випадку bsearch()
або сортованих у випадку qsort()
, не потрібно знати, коли функція використовується б / в.
Наприклад, ось невелика прикладна програма з bsearch()
використанням різних функцій порівняння, синхронних зворотних зворотів. Дозволяючи нам делегувати порівняння даних функції зворотного виклику,bsearch()
функція дозволяє нам вирішувати під час виконання, яке порівняння ми хочемо використовувати. Це синхронно, тому що коли bsearch()
функція повертається, завдання завершено.
#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);
}
}
Асинхронний зворотний виклик відрізняється тим, що коли повертається викликана функція, до якої ми надаємо зворотний виклик, завдання може не бути виконаним. Цей тип зворотного виклику часто використовується з асинхронним входом / виводом, в якому запускається операція вводу / виводу, а потім після її завершення викликається зворотний виклик.
У наступній програмі ми створюємо сокет для прослуховування запитів на підключення TCP, і коли запит отримано, функція, що прослуховує, викликає надану функцію зворотного виклику. Цей простий додаток можна використовувати, запустивши його в одному вікні, використовуючиtelnet
утиліту або веб-браузер, щоб спробувати підключитися в іншому вікні.
Я зняв більшу частину коду WinSock із прикладу, який Microsoft забезпечує accept()
функцією за адресою https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
Ця програма запускає listen()
локальний хост, 127.0.0.1, використовуючи порт 8282, щоб ви могли використовувати або telnet 127.0.0.1 8282
абоhttp://127.0.0.1:8282/
.
Цей зразок програми створено як консольний додаток із Visual Studio 2017 Community Edition, і він використовує версію розеток Microsoft WinSock. Для додатка Linux функції WinSock потрібно буде замінити на альтернативи Linux, а pthreads
замість цього використовуватиметься бібліотека потоків Windows .
#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);
}