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

Beej's Guide to Network Programming 系列連載 11:connect() — 客戶端發起連線

     編輯:關於C語言

前言:客戶端的核心動作

connect() 是客戶端主動發起 TCP 連線的關鍵系統呼叫。與伺服器端的 bind()+listen()+accept() 不同,客戶端不需 bind(),作業系統會自動挑選可用的暫時埠號 (ephemeral port) 並綁定。

函數原型

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

參數說明

  • sockfd:socket() 回傳的檔案描述符
  • serv_addr:目標伺服器位址結構 (含 IP 與埠號)
  • addrlen:結構大小

現代實戰範例:連線至 www.example.com:3490

struct addrinfo hints, *res;
int sockfd;

// 1. 設定 hints (客戶端不需 AI_PASSIVE)
memset(&hints, 0, sizeof(hints));
hints.ai_family   = AF_UNSPEC;     // IPv4 或 IPv6
hints.ai_socktype = SOCK_STREAM;   // TCP

// 2. 解析目標位址
getaddrinfo("www.example.com", "3490", &hints, &res);

// 3. 建立 socket
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

// 4. 發起連線 - 直接使用 res 內容
connect(sockfd, res->ai_addr, res->ai_addrlen);

// 5. 連線成功!現在可用 send()/recv() 通訊
freeaddrinfo(res);

為什麼客戶端不需要 bind()?

  • 作業系統在 connect() 時自動從暫時埠號範圍 (通常 32768-60999) 挑選一個未使用的埠號
  • 此埠號僅作為來源埠號,伺服器端透過封包自動得知
  • 連線關閉後埠號釋放,重複使用

進階:遍歷多重位址嘗試連線

getaddrinfo() 可能回傳多個位址 (IPv4 + IPv6),健壯的客戶端應依序嘗試直到成功:

int sockfd = -1;
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == -1) continue;
    
    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == 0) {
        break;  // 成功
    }
    close(sockfd);
    sockfd = -1;
}
if (sockfd == -1) {
    // 所有位址都連線失敗
}
freeaddrinfo(res);

回傳值與錯誤處理

  • 成功:回傳 0
  • 失敗:回傳 -1,設定 errno
  • 常見錯誤:
    • ECONNREFUSED:目標埠口無程式監聽
    • ETIMEDOUT:連線逾時 (網路不通、防火牆阻擋)
    • ENETUNREACH:網路不可達
    • EADDRNOTAVAIL:無法指派來源位址

非阻塞連線 (Non-blocking Connect)

若不想讓 connect() 阻塞主執行緒:

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

int ret = connect(sockfd, res->ai_addr, res->ai_addrlen);
if (ret == -1 && errno == EINPROGRESS) {
    // 連線進行中,使用 select()/poll()/epoll() 等待可寫事件
    // 可寫時表示連線完成 (或失敗),需用 getsockopt(SO_ERROR) 檢查
}

結語

connect() 是客戶端「撥號」的動作。現代寫法同樣依賴 getaddrinfo() 取得位址資訊,**零硬編碼、自動支援 IPv4/IPv6、健壯遍歷多重位址**。掌握同步與非阻塞兩種模式,能應對從簡單 CLI 工具到高併發伺服器的各種場景。下一章將探討伺服器端的 listen() 與 accept()。

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