首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

跨平台 Socket 非阻塞模式設定:Windows 與 Linux 完整指南

     編輯:C++ 入門

前言

非阻塞 Socket 是高併發網路程式設計的基石。本文詳細說明 Windows 與 Linux 設定 Socket 非阻塞模式的標準方法,並深入解析 Linux fcntl 系統呼叫的完整功能。

Windows 平台

使用 ioctlsocket 配合 FIONBIO 指令:

unsigned long ul = 1;  // 1 = 非阻塞,0 = 阻塞
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
int ret = ioctlsocket(s, FIONBIO, &ul);
if (ret == SOCKET_ERROR) {
    // 錯誤處理
    int err = WSAGetLastError();
}

關鍵點:參數為指向 unsigned long 的指標,非零值啟用非阻塞模式。

Linux 平台

使用 fcntl 修改檔案狀態旗標 O_NONBLOCK:

// 設為非阻塞
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags | O_NONBLOCK);

// 恢復阻塞模式
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, flags & ~O_NONBLOCK);

核心邏輯:先取得目前旗標,再透過位元運算增減 O_NONBLOCK,最後寫回。

fcntl 完整功能詳解

fcntl 依據 cmd 參數提供豐富的檔案描述符控制功能:

1. 檔案描述符複製與關閉旗標

  • F_DUPFD:複製檔案描述符
  • FD_CLOEXEC:exec 時自動關閉 (配合 F_SETFD/F_GETFD)

2. 檔案狀態旗標 (F_GETFL/F_SETFL)

可修改旗標:O_APPEND、O_ASYNC、O_DIRECT、O_NOATIME、O_NONBLOCK
不可修改:O_RDONLY、O_WRONLY、O_RDWR、O_CREAT、O_EXCL、O_NOCTTY、O_TRUNC

3. 記錄鎖管理 (F_GETLK/F_SETLK/F_SETLKW)

struct flock {
    short l_type;    // F_RDLCK(讀鎖), F_WRLCK(寫鎖), F_UNLCK(解鎖)
    short l_whence;  // SEEK_SET, SEEK_CUR, SEEK_END
    off_t l_start;   // 相對偏移
    off_t l_len;     // 鎖定長度 (0=到檔案結尾)
    pid_t l_pid;     // 持有鎖的進程 ID (F_GETLK 回傳)
};
  • F_SETLK:非阻塞加/解鎖,衝突即返回 EACCES/EAGAIN
  • F_SETLKW:阻塞等待鎖,可被訊號中斷 (EINTR)
  • F_GETLK:檢測鎖衝突

前置條件:讀鎖需以讀模式開啟檔案,寫鎖需寫模式,讀寫鎖需讀寫模式。

4. 訊號驅動 I/O (F_GETOWN/F_SETOWN/F_GETSIG/F_SETSIG)

設定接收 SIGIO/SIGURG 的進程/進程組,配合 F_SETSIG 可指定自訂訊號,實現完整非同步 I/O,無需 select/poll。

5. 檔案租約 (F_SETLEASE/F_GETLEASE)

  • F_RDLCK:讀租約 — 其他進程寫入/截斷時通知
  • F_WRLCK:寫租約 — 其他進程讀取/寫入/截斷時通知
  • F_UNLCK:移除租約

6. 目錄/檔案變更通知 (Linux 2.4+)

參數為位元組合:DN_ACCESS、DN_MODIFY、DN_CREATE、DN_DELETE、DN_RENAME、DN_ATTRIB。監控目錄下檔案變更事件。

返回值與錯誤碼

命令成功返回
F_DUPFD新檔案描述符
F_GETFD/F_GETFL旗標值
F_GETOWN擁有者 PID/PGID
F_GETSIG訊號編號
其他0

失敗統一返回 -1,errno 可能值:EACCES/EAGAIN、EBADF、EDEADLK、EFAULT、EINTR、EINVAL、EMFILE、ENOLCK、EPERM。

跨平台封裝建議

bool SetNonBlocking(SOCKET sock) {
#ifdef _WIN32
    unsigned long mode = 1;
    return ioctlsocket(sock, FIONBIO, &mode) != SOCKET_ERROR;
#else
    int flags = fcntl(sock, F_GETFL, 0);
    if (flags == -1) return false;
    return fcntl(sock, F_SETFL, flags | O_NONBLOCK) != -1;
#endif
}

總結

Windows 使用 ioctlsocket、Linux 使用 fcntl 設定非阻塞模式,兩者機制不同但目的一致。fcntl 更是 Linux 檔案描述符控制的瑞士刀,掌握其完整功能對系統級程式設計至關重要。建議封裝跨平台介面,統一上層呼叫介面。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved