การเรียกกลับใน C คือฟังก์ชั่นที่มีให้กับฟังก์ชั่นอื่นเพื่อ "โทรกลับไปที่" ในบางจุดเมื่อฟังก์ชั่นอื่น ๆ ที่จะทำงานของมัน
มีสองวิธีที่ใช้โทรกลับ : โทรกลับซิงโครนัสและโทรกลับไม่ตรงกัน การโทรกลับแบบซิงโครนัสมีให้ในฟังก์ชันอื่นซึ่งกำลังจะทำงานบางอย่างจากนั้นกลับไปที่ผู้โทรโดยงานจะเสร็จสมบูรณ์ การโทรกลับแบบอะซิงโครนัสมีให้ในฟังก์ชันอื่นซึ่งกำลังจะเริ่มงานจากนั้นกลับไปที่ผู้โทรด้วยงานที่อาจไม่เสร็จสมบูรณ์
โดยปกติแล้วการโทรกลับแบบซิงโครนัสจะใช้เพื่อมอบตัวแทนให้กับฟังก์ชันอื่นซึ่งฟังก์ชันอื่นมอบหมายหน้าที่บางขั้นตอนให้กับงาน ตัวอย่างคลาสสิกของการมอบหมายนี้คือฟังก์ชั่นbsearch()
และqsort()
จาก C Standard Library ทั้งสองฟังก์ชั่นเหล่านี้ใช้การเรียกกลับที่ใช้ในระหว่างงานฟังก์ชั่นให้เพื่อให้ชนิดของข้อมูลที่ถูกค้นหาในกรณีของ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);
}
}
การเรียกกลับแบบอะซิงโครนัสแตกต่างกันเมื่อฟังก์ชั่นที่เรียกใช้ซึ่งเราจัดเตรียมการโทรกลับส่งคืนงานอาจไม่เสร็จสมบูรณ์ การเรียกกลับชนิดนี้มักใช้กับ I / O แบบอะซิงโครนัสซึ่งการดำเนินการ I / O เริ่มต้นและจากนั้นเมื่อดำเนินการเสร็จสิ้นการเรียกกลับจะถูกเรียกใช้
ในโปรแกรมต่อไปนี้เราสร้างซ็อกเก็ตเพื่อฟังการร้องขอการเชื่อมต่อ 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 และไลบรารีเธรด Windows จะใช้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);
}