C Academy · บทเรียน

การจัดการไคลเอ็นต์หลายราย

select และ poll

บทเรียน 4 จาก 413 ขั้นตอน

การจัดการไคลเอ็นต์หลายราย เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ภาพรวมซ็อกเก็ต
  2. เซิร์ฟเวอร์ TCP
  3. ไคลเอ็นต์ TCP
  4. การจัดการไคลเอ็นต์หลายราย
← กลับไปที่ C Academy