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

原始 Socket 實現 TCP 端口掃描:非阻塞連接與 select 模型高併發

     編輯:C++ 入門

為什麼不用 CSocket

CSocket 封裝了阻塞模式與消息泵,高併發下線程開銷大。原始 Socket + select/WSAEventSelect 可單線程管理萬級並發連接。

核心非阻塞連接邏輯

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
u_long mode = 1;
ioctlsocket(sock, FIONBIO, &mode); // 設置非阻塞

sockaddr_in addr = {AF_INET, htons(port), inet_addr(ip)};
int ret = connect(sock, (sockaddr*)&addr, sizeof(addr));
if (ret == SOCKET_ERROR) {
    int err = WSAGetLastError();
    if (err != WSAEWOULDBLOCK) { closesocket(sock); return; }
    // 連接發起中,加入 select 寫集合等待可寫
}

fd_set writeSet;
FD_ZERO(&writeSet);
FD_SET(sock, &writeSet);
timeval tv = {2, 0}; // 2秒超時
if (select(0, NULL, &writeSet, NULL, &tv) > 0 && FD_ISSET(sock, &writeSet)) {
    int err = 0; int len = sizeof(err);
    getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
    if (err == 0) { /* 連接成功 */ }
}
closesocket(sock);

select 模型批量掃描框架

std::vector<SOCKET> sockets;
for (port = start; port <= end; ++port) {
    SOCKET s = socket(...);
    ioctlsocket(s, FIONBIO, &mode);
    connect(s, ...); // 發起所有連接
    sockets.push_back(s);
}

while (!sockets.empty()) {
    fd_set writeSet; FD_ZERO(&writeSet);
    for (auto s : sockets) FD_SET(s, &writeSet);
    if (select(0, NULL, &writeSet, NULL, &tv) <= 0) break; // 超時

    for (auto it = sockets.begin(); it != sockets.end(); ) {
        if (FD_ISSET(*it, &writeSet)) {
            // 檢查 SO_ERROR 確認結果
            it = sockets.erase(it);
        } else ++it;
    }
}

服務識別與結果輸出

同 getservbyport 解析知名端口,結果寫入文件或 UI。

優勢總結

  • 單線程處理 10000+ 並發連接,內存/CPU 開銷極低
  • 精確控制超時、重試、發包間隔
  • 可擴展為 SYN 掃描、服務版本探測

下載地址

見後續博文置頂下載鏈接。

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