การจัดการไคลเอ็นต์หลายราย
select และ poll
การจัดการไคลเอ็นต์หลายราย เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
ปัญหาการทำงานพร้อมกัน
เซิร์ฟเวอร์อย่างง่ายจะจัดการไคลเอ็นต์ทีละราย หากไคลเอ็นต์รายหนึ่งทำงานช้า รายอื่นทั้งหมดก็ต้องรอ หากต้องการให้บริการ ไคลเอ็นต์จำนวนมากพร้อมกัน คุณจำเป็นต้องใช้รูปแบบอื่น
แนวทางสำหรับการทำงานพร้อมกัน
กลยุทธ์ทั่วไปมีสามแบบ:
- หนึ่งเธรดหรือกระบวนการต่อไคลเอ็นต์: เรียบง่าย แต่ใช้ทรัพยากรมากเมื่อมีขนาดใหญ่
- การทำมัลติเพล็กซ์อินพุต/เอาต์พุต ด้วย
selectหรือpoll: เธรดเดียวเฝ้าดูซ็อกเก็ตจำนวนมาก - ลูปเหตุการณ์ ด้วย
epoll/kqueue: รองรับการขยายไปถึงหลักพัน
สิ่งที่ select ทำ
select() เฝ้าดูชุดตัวบอกไฟล์ และแจ้งว่าตัวใด พร้อม สำหรับการอ่าน การเขียน หรือมีข้อผิดพลาด ทั้งหมดนี้ทำในหนึ่งการเรียกที่บล็อกการทำงาน จึงทำให้เธรดเดียวให้บริการซ็อกเก็ตจำนวนมากได้
พื้นฐานของ fd_set
select ใช้บิตแมสก์ fd_set โดยจัดการผ่านแมโครเหล่านี้: FD_ZERO, FD_SET, FD_CLR และ FD_ISSET
#include <sys/select.h>
void setup_set(fd_set *set, int listen_fd) {
FD_ZERO(set); /* clear all */
FD_SET(listen_fd, set); /* watch the listener */
}การเรียกใช้ select
select ต้องใช้ค่าตัวบอกไฟล์สูงสุดบวกหนึ่ง และแก้ไขชุดดังกล่าวโดยตรงเพื่อทำเครื่องหมายตัวบอกที่พร้อมทำงาน เนื่องจากการเรียกนี้ทำลายข้อมูลเดิม คุณจึงต้องสร้างชุดขึ้นใหม่ก่อนการเรียกแต่ละครั้ง
#include <sys/select.h>
#include <stdio.h>
int wait_ready(int maxfd, fd_set *read_set) {
int n = select(maxfd + 1, read_set, NULL, NULL, NULL);
if (n < 0) perror("select");
return n; /* number of ready fds */
}ลูปเซิร์ฟเวอร์แบบ select
รูปแบบการทำงานคือ สร้างชุดขึ้นใหม่ เรียก select จากนั้นตรวจสอบตัวบอกไฟล์แต่ละตัวที่พร้อมทำงาน โดยรับไคลเอ็นต์ใหม่หากเป็นตัวฟัง หรืออ่านข้อมูลหากเป็นไคลเอ็นต์
#include <sys/select.h>
void loop_skeleton(int listen_fd, fd_set *master, int maxfd) {
fd_set work;
for (;;) {
work = *master; /* copy, select destroys it */
select(maxfd + 1, &work, NULL, NULL, NULL);
for (int fd = 0; fd <= maxfd; fd++) {
if (!FD_ISSET(fd, &work)) continue;
/* fd == listen_fd -> accept; else -> recv */
}
}
}การติดตามไคลเอ็นต์
เมื่อรับการเชื่อมต่อใหม่ ให้เพิ่มตัวบอกไฟล์ของการเชื่อมต่อนั้นลงในชุดหลักและอัปเดต maxfd เมื่อไคลเอ็นต์ตัดการเชื่อมต่อ (recv คืนค่า 0) ให้เรียก close และนำตัวบอกไฟล์นั้นออกจากชุดด้วย FD_CLR
#include <sys/select.h>
#include <unistd.h>
void add_client(fd_set *master, int *maxfd, int conn) {
FD_SET(conn, master);
if (conn > *maxfd) *maxfd = conn;
}
void drop_client(fd_set *master, int conn) {
close(conn);
FD_CLR(conn, master);
}ข้อจำกัดของ select
select มีข้อเสียหลายประการ:
- จำกัดจำนวนตัวบอกไฟล์ไว้ที่
FD_SETUP(มักเป็น 1024) - ต้องตรวจสอบตัวบอกไฟล์ทั้งหมดด้วยเวลา O(n) ในทุกการเรียก
- ต้องสร้างชุดขึ้นใหม่ในทุกการทำซ้ำ
สำหรับการเชื่อมต่อจำนวนมาก poll หรือ epoll รองรับการขยายได้ดีกว่า
สิ่งที่ poll มีให้
poll() รับอาร์เรย์ของ struct pollfd แทนบิตแมสก์แบบตายตัว จึงไม่มีข้อจำกัดที่ 1024 ตัวบอกไฟล์ และไม่ทำลายข้อมูลอินพุตในการเรียกแต่ละครั้ง
#include <poll.h>
void setup_poll(struct pollfd *pfd, int listen_fd) {
pfd[0].fd = listen_fd;
pfd[0].events = POLLIN; /* notify when readable */
}การเรียกใช้ poll
poll จะบล็อกจนกว่าจะมีตัวบอกไฟล์อย่างน้อยหนึ่งตัวพร้อมทำงาน (หรือหมดเวลา) จากนั้นจะกำหนดค่า revents ให้กับรายการที่พร้อมทำงานแต่ละรายการ
#include <poll.h>
#include <stdio.h>
int poll_ready(struct pollfd *pfds, int count) {
int n = poll(pfds, count, -1); /* -1 = block forever */
if (n < 0) perror("poll");
return n;
}การขยายระบบ: epoll
สำหรับการเชื่อมต่อหลายพันรายการ epoll ของ Linux (และ kqueue ของ BSD) จะส่งคืนเฉพาะตัวบอกไฟล์ที่พร้อมทำงาน โดยใช้เวลา O(1) ต่อเหตุการณ์ จึงหลีกเลี่ยงการตรวจสอบทั้งหมดที่ select และ poll ทำ
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับการจัดการไคลเอ็นต์หลายราย
สรุปทบทวน
คุณได้เรียนรู้วิธีให้บริการไคลเอ็นต์จำนวนมากด้วยเธรดเดียว
selectเฝ้าดูตัวบอกไฟล์จำนวนมาก โดยจัดการด้วยแมโคร FD_- สร้างชุดขึ้นใหม่ในทุกลูป เนื่องจาก select ทำลายชุดเดิม
- เพิ่มไคลเอ็นต์ที่รับการเชื่อมต่อแล้วลงในชุด และนำออกเมื่อตัดการเชื่อมต่อ
pollหลีกเลี่ยงข้อจำกัดที่ 1024 รายการ ส่วนepollรองรับการขยายไปถึงหลักพัน
เรียนรู้ C ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 39
- บทเรียน
- 144
คำถามที่พบบ่อย
บทเรียน “การจัดการไคลเอ็นต์หลายราย” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การจัดการไคลเอ็นต์หลายราย” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การจัดการไคลเอ็นต์หลายราย”
select และ poll คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การจัดการไคลเอ็นต์หลายราย” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม
ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ภาพรวมซ็อกเก็ต
- เซิร์ฟเวอร์ TCP
- ไคลเอ็นต์ TCP
- การจัดการไคลเอ็นต์หลายราย