Ein Rückruf in C ist eine Funktion, die einer anderen Funktion zur Verfügung gestellt wird, um sie irgendwann zurückzurufen, wenn die andere Funktion ihre Aufgabe erfüllt.
Es gibt zwei Möglichkeiten, wie ein Rückruf verwendet wird : synchroner Rückruf und asynchroner Rückruf. Ein synchroner Rückruf wird an eine andere Funktion gesendet, die eine Aufgabe ausführt und dann mit abgeschlossener Aufgabe zum Anrufer zurückkehrt. Ein asynchroner Rückruf wird an eine andere Funktion gesendet, die eine Aufgabe startet und dann mit möglicherweise nicht abgeschlossener Aufgabe zum Aufrufer zurückkehrt.
Ein synchroner Rückruf wird normalerweise verwendet, um einen Delegaten an eine andere Funktion bereitzustellen, an die die andere Funktion einen Schritt der Aufgabe delegiert. Klassische Beispiele für diese Delegation sind die Funktionen bsearch()
und qsort()
aus der C-Standardbibliothek. Diese beiden Funktionen nehmen einen Rückruf entgegen, der während der von der Funktion bereitgestellten Aufgabe verwendet wird, so dass der Typ der Daten im Fall von durchsucht oder im Fall von bsearch()
sortiert wirdqsort()
der Funktion nicht bekannt sein muss gebraucht.
Zum Beispiel ist hier ein kleines Beispielprogramm mit bsearch()
verschiedenen Vergleichsfunktionen, synchronen Rückrufen. Indem wir den Datenvergleich an eine Rückruffunktion delegieren können, bsearch()
können wir zur Laufzeit entscheiden, welche Art von Vergleich wir verwenden möchten. Dies ist synchron, da bsearch()
die Aufgabe abgeschlossen ist, wenn die Funktion zurückgegeben wird.
#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);
}
}
Ein asynchroner Rückruf unterscheidet sich darin, dass die Aufgabe möglicherweise nicht abgeschlossen wird, wenn die aufgerufene Funktion, für die wir einen Rückruf bereitstellen, zurückkehrt. Diese Art von Rückruf wird häufig bei asynchronen E / A verwendet, bei denen eine E / A-Operation gestartet wird. Wenn sie abgeschlossen ist, wird der Rückruf aufgerufen.
Im folgenden Programm erstellen wir einen Socket zum Abhören von TCP-Verbindungsanforderungen. Wenn eine Anforderung empfangen wird, ruft die Funktion, die das Abhören ausführt, die bereitgestellte Rückruffunktion auf. Diese einfache Anwendung kann ausgeführt werden, indem Sie sie in einem Fenster ausführen, während Sie mit dem telnet
Dienstprogramm oder einem Webbrowser versuchen, eine Verbindung in einem anderen Fenster herzustellen.
Ich habe den größten Teil des WinSock-Codes aus dem Beispiel entfernt, das Microsoft mit der accept()
Funktion unter https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx bereitstellt
Diese Anwendung startet a listen()
auf dem lokalen Host 127.0.0.1 über Port 8282, sodass Sie entweder telnet 127.0.0.1 8282
oder verwenden können http://127.0.0.1:8282/
.
Diese Beispielanwendung wurde als Konsolenanwendung mit Visual Studio 2017 Community Edition erstellt und verwendet die Microsoft WinSock-Version von Sockets. Für eine Linux-Anwendung müssten die WinSock-Funktionen durch die Linux-Alternativen ersetzt werden, und pthreads
stattdessen würde die Windows-Thread-Bibliothek verwendet .
#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);
}