4.1 概述
本章講解編寫乙個完整的tcp客戶/伺服器程式所需要的基本套接字函式,
為下一章編寫客戶/伺服器程式及其擴充套件做準備。
基本tcp客戶/伺服器程式的而套接字呼叫流程圖如下:
4.2 socket函式
為了執行網路i/o,乙個程序必須做的第一件事情就是呼叫socket函式
#include int socket(int family, int type, int protocol);
socket函式在成功時返回乙個小的非負整數值,它與檔案描述符類似,
我們把它稱為套接字描述符(socket descriptor),簡稱sockfd。
其中family引數指明協議族
family
說明af_inet
ipv4協議
af_inet6
ipv6協議
af_local
unix域協議
af_route
路由套接字
af_key
秘鑰套接字
type引數指明套接字型別
type
說明sock_stream
位元組流套接字
sock_dgram
資料報套接字
sock_seqpacket
有序分組套接字
sock_raw
原始套接字
protocol引數指明使用的協議
protocol
說明ipproto_tcp
tcp傳輸協議
ipproto_udp
udp傳輸協議
ipproto_sctp
sctp傳輸協議
4.3 connec函式
tcp客戶用connect函式來建立與tcp伺服器的連線。
#include int connect(int sockfd, const struct sockaddr *servaddr, sockeln_t addrlen);
sockfd是由socket函式返回的套接字描述符,第二個、第三個引數分別是乙個指向套接字位址結構的指標和該結構的大小。
套接字位址結構必須含有伺服器的ip的位址和埠號
客戶在呼叫函式connect前不必非得呼叫bind函式,因為如果需要的話,核心會確定源ip位址,並選擇乙個臨時埠作為源埠。
4.4 bind函式
bind函式把乙個本地協議位址賦予乙個套接字。對於網際協議,
協議位址是32位的ipv4位址或128位的ipv6位址與16位的tcp或udp埠號的組合。
#include int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
//返回:成功為0,出錯為-1
第二個引數是乙個指向特定於協議的位址結構的指標,第三個引數是該位址結構的長度
對於tcp,呼叫bind函式可以指定乙個埠號,或指定乙個ip位址,也可以兩者都指定,還可以都不指定。
4.5 listen函式
listen函式僅由tcp伺服器呼叫,它做兩件事情
(1)將socket函式建立的主動套接字轉換成乙個被動套接字,listen導致套接字由close狀態轉換到listen狀態
(2)通過第二個引數指定核心應該為該套接字排隊的最大連線個數
#include int listen(int sockfd, int backlog); //成功返回0,出錯返回-1
4.6 accept函式
accept函式由tcp伺服器呼叫,用於從已完成連線佇列隊頭返回下乙個已完成連線。
如果已完成連線隊列為空,那麼程序被投入睡眠(假定套接字為預設的阻塞方式)。
#include int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
// 成功返回非負描述符,出錯返回-1
4.7 fork和exec函式
fork函式是unix中派生新程序的唯一方法。
#include pid_t fork(void);
呼叫fork函式,在子程序中返回0,父程序中返回子程序的程序id號。
fork有兩個典型用法。
(1)乙個程序建立乙個自身的副本,這樣每個副本都可以在另乙個副本執行其他任務的同時處理各自的某個操作。
這是網路伺服器的典型用法。
(2)乙個程序想要執行另乙個程式。既然建立新程序的唯一辦法是呼叫fork,該程序於是首先呼叫fork函式建立乙個自身的副本,然後其中乙個副本(通常為子程序)呼叫exec,把自身替換成新的程式,這是諸如shell之類程式的典型用法。
4.8 併發伺服器
當服務乙個客戶請求可能花費較長時間時,我們並不希望整個伺服器被單個客戶長期占用,
而希望同時服務多個客戶。unix中編寫併發伺服器程式最簡單的辦法就是fork乙個子程序來服務每個客戶。
// 典型的併發伺服器程式輪廓
pid_t pid;
int listenfd, connfd;
listenfd = socket( ... );
/* fill in sockaddr_in{} with server's well-known port */
bind(listenfd, ... );
listen(listenfd, listenq);
for ( ; ; )
close(connfd); /* parent closes connected socket */
}
呼叫fork後,對connfd的引用計數由1變為2,所以在父程序中close(connfd)關閉父程序已連線套接字,會使2變為1,
並不會導致與客戶的連線斷開。
此外,我們在子程序中顯式呼叫close也不是必須的,因為在呼叫exit後,程序終止處理的部分會關閉所有由核心開啟的描述符。
4.9 close函式
close乙個tcp套接字的預設行為是把該套接字標記成已關閉。
如果該描述符的引用計數大於1,則會使其引用計數減1。如果減1後依然大於0,則不會引發tcp的四分組連線終止序列。
如果我們確實像在某個tcp連線上傳送乙個fin,可以改用shutdown函式代替close。
4.10 getsockname和getpeername函式
這兩個函式或者返回與某個套接字關聯的本地協議位址(getsockname),
或者返回與某個套接字關聯的外地協議位址(getpeername)
#include int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
4.11 小結
大多數tcp伺服器是併發的,他們為每個待處理的客戶連線呼叫fork派生乙個子程序。
大多數udp伺服器是迭代的。
儘管這兩個模型已經成功地運用了許多年,我們仍將在之後**使用執行緒和程序的其他伺服器程式設計方法。
UNIX網路程式設計卷1 第1章 簡介
要編寫通過計算機網路通訊的程式,首先要確定這些程式相互通訊所用的協議。一般認為web伺服器程式是乙個長時間執行的程式 守護程式,daemon 它只在響應來自網路的請求時才傳送網路訊息。協議的另一端是web客戶程式,如某種瀏覽器,與伺服器程序的通訊總是由客戶程序發起。在設計網路應用時,確定總是由客戶發...
套接字程式設計基礎 Unix網路程式設計第3章總結
1.套接字位址結構 以下是其posix定義 struct in addr struct sockaddr in在rhel5中,in addr t是無符號32位整數,in.h檔案中包含 typedef unit32 t in addr t posix規範只有sin family,sin port和si...
檔案和目錄 UNIX環境高階程式設計 第4章
4.2 stat fstat和lstat函式 int stat const char restrict pathname,struct stat restrict buf int fstat int filedes,struct stat buf int lstat const char restr...