自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基本 TCP 套接字編程講解

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
基于 TCP 的套接字編程的所有客戶端和服務(wù)器端都是從調(diào)用socket 開始,它返回一個套接字描述符??蛻舳穗S后調(diào)用connect 函數(shù),服務(wù)器端則調(diào)用 bind、listen 和accept 函數(shù)。

基本 TCP 套接字編程講解

基于 TCP 的套接字編程的所有客戶端和服務(wù)器端都是從調(diào)用socket 開始,它返回一個套接字描述符??蛻舳穗S后調(diào)用connect 函數(shù),服務(wù)器端則調(diào)用 bind、listen 和accept 函數(shù)。套接字通常使用標(biāo)準(zhǔn)的close 函數(shù)關(guān)閉,但是也可以使用 shutdown 函數(shù)關(guān)閉套接字。下面針對套接字編程實(shí)現(xiàn)過程中所調(diào)用的函數(shù)進(jìn)程分析。以下是基于 TCP 套接字編程的流程圖:

 

 

socket 函數(shù)

套接字是通信端點(diǎn)的抽象,實(shí)現(xiàn)端對端之間的通信。與應(yīng)用程序要使用文件描述符訪問文件一樣,訪問套接字需要套接字描述符。任何套接字編程都必須調(diào)用socket 函數(shù)獲得套接字描述符,這樣才能對套接字進(jìn)行操作。以下是該函數(shù)的描述:

  1. /* 套接字 */   
  2.    
  3. /*  
  4.  * 函數(shù)功能:創(chuàng)建套接字描述符;  
  5.  * 返回值:若成功則返回套接字非負(fù)描述符,若出錯返回-1;  
  6.  * 函數(shù)原型:  
  7.  */   
  8. #include <sys/socket.h>   
  9.    
  10. int socket(int family, int type, int protocol);   
  11. /*  
  12.  * 說明:  
  13.  * socket類似與open對普通文件操作一樣,都是返回描述符,后續(xù)的操作都是基于該描述符;  
  14.  * family 表示套接字的通信域,不同的取值決定了socket的地址類型,其一般取值如下:  
  15.  * (1)AF_INET         IPv4因特網(wǎng)域  
  16.  * (2)AF_INET6        IPv6因特網(wǎng)域  
  17.  * (3)AF_UNIX         Unix域  
  18.  * (4)AF_ROUTE        路由套接字  
  19.  * (5)AF_KEY          密鑰套接字  
  20.  * (6)AF_UNSPEC       未指定  
  21.  *  
  22.  * type確定socket的類型,常用類型如下:  
  23.  * (1)SOCK_STREAM     有序、可靠、雙向的面向連接字節(jié)流套接字  
  24.  * (2)SOCK_DGRAM      長度固定的、無連接的不可靠數(shù)據(jù)報(bào)套接字  
  25.  * (3)SOCK_RAW        原始套接字  
  26.  * (4)SOCK_SEQPACKET  長度固定、有序、可靠的面向連接的有序分組套接字  
  27.  *  
  28.  * protocol指定協(xié)議,常用取值如下:  
  29.  * (1)0               選擇type類型對應(yīng)的默認(rèn)協(xié)議  
  30.  * (2)IPPROTO_TCP     TCP傳輸協(xié)議  
  31.  * (3)IPPROTO_UDP     UDP傳輸協(xié)議  
  32.  * (4)IPPROTO_SCTP    SCTP傳輸協(xié)議  
  33.  * (5)IPPROTO_TIPC    TIPC傳輸協(xié)議  
  34.  *  
  35.  */   

connect 函數(shù)

在處理面向連接的網(wǎng)絡(luò)服務(wù)時,例如 TCP ,交換數(shù)據(jù)之前必須在請求的進(jìn)程套接字和提供服務(wù)的進(jìn)程套接字之間建立連接。TCP 客戶端可以調(diào)用函數(shù)connect 來建立與 TCP 服務(wù)器端的一個連接。該函數(shù)的描述如下:

  1. /*  
  2.  * 函數(shù)功能:建立連接,即客戶端使用該函數(shù)來建立與服務(wù)器的連接;  
  3.  * 返回值:若成功則返回0,出錯則返回-1;  
  4.  * 函數(shù)原型:  
  5.  */   
  6. #include <sys/socket.h>   
  7.    
  8. int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);   
  9. /*  
  10.  * 說明:  
  11.  * sockfd是系統(tǒng)調(diào)用的套接字描述符,即由socket函數(shù)返回的套接字描述符;  
  12.  * servaddr是目的套接字的地址,該套接字地址結(jié)構(gòu)必須包含目的IP地址和目的端口號,即想與之通信的服務(wù)器地址;  
  13.  * addrlen是目的套接字地址的大??;  
  14.  *  
  15.  * 如果sockfd沒有綁定到一個地址,connect會給調(diào)用者綁定一個默認(rèn)地址,即內(nèi)核會確定源IP地址,并選擇一個臨時端口號作為源端口號;  
  16.  */   

TCP 客戶端在調(diào)用函數(shù) connect 前不必非得調(diào)用 bind 函數(shù),因?yàn)閮?nèi)核會確定源 IP 地址,并選擇一個臨時端口作為源端口號。若 TCP 套接字調(diào)用connect 函數(shù)將建立 TCP 連接(執(zhí)行三次握手),而且僅在連接建立成功或出錯時才返回,其中出錯返回可能有以下幾種情況:

若 TCP 客戶端沒有收到 SYN 報(bào)文段的響應(yīng),則返回 ETIMEOUT 錯誤;

若客戶端的 SYN 報(bào)文段的響應(yīng)是 RST (表示復(fù)位),則表明該服務(wù)器主機(jī)在我們指定的端口上沒有進(jìn)程在等待與之連接。只是一種硬錯誤,客戶端一接收到 RST 就立即返回ECONNERFUSED 錯誤;

RST 是 TCP 在發(fā)生錯誤時發(fā)送的一種 TCP 報(bào)文段。產(chǎn)生 RST 的三個條件時:

目的地為某端口的 SYN 到達(dá),然而該端口上沒有正在監(jiān)聽的服務(wù)器;

TCP 想取消一個已有連接;

TCP 接收到一個不存在的連接上的報(bào)文段;

若客戶端發(fā)出的 SYN 在中某個路由器上引發(fā)一個目的地不可達(dá)的 ICMP 錯誤,這是一個軟錯誤??蛻舳酥鳈C(jī)內(nèi)核保存該消息,并在一定的時間間隔繼續(xù)發(fā)送 SYN (即重發(fā))。在某規(guī)定的時間后仍未收到響應(yīng),則把保存的消息(即 ICMP 錯誤)作為EHOSTUNREACH 或ENETUNREACH 錯誤返回給進(jìn)行。#p#

bind 函數(shù)

調(diào)用函數(shù) socket 創(chuàng)建套接字描述符時,該套接字描述符是存儲在它的協(xié)議族空間中,沒有具體的地址,要使它與一個地址相關(guān)聯(lián),可以調(diào)用函數(shù)bind 使其與地址綁定。客戶端的套接字關(guān)聯(lián)的地址一般可由系統(tǒng)默認(rèn)分配,因此不需要指定具體的地址。若要為服務(wù)器端套接字綁定地址,可以通過調(diào)用函數(shù) bind 將套接字綁定到一個地址。下面是該函數(shù)的描述:

  1. /* 套接字的基本操作 */   
  2.    
  3. /*  
  4.  * 函數(shù)功能:將協(xié)議地址綁定到一個套接字;其中協(xié)議地址包含IP地址和端口號;  
  5.  * 返回值:若成功則返回0,若出錯則返回-1;  
  6.  * 函數(shù)原型:  
  7.  */   
  8. #include <sys/socket.h>   
  9. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);   
  10. /*  
  11.  * 說明:  
  12.  * sockfd 為套接字描述符;  
  13.  * addr是一個指向特定協(xié)議地址結(jié)構(gòu)的指針;  
  14.  * addrlen是地址結(jié)構(gòu)的長度;  
  15.  */   

對于 TCP 協(xié)議,調(diào)用 bind 函數(shù)可以指定一個端口號,或指定一個 IP 地址,也可以兩者都指定,還可以都不指定。若 TCP 客戶端或服務(wù)器端不調(diào)用bind 函數(shù)綁定一個端口號,當(dāng)調(diào)用connect 或 listen 函數(shù)時,內(nèi)核會為相應(yīng)的套接字選擇一個臨時端口號。一般 TCP 客戶端使用內(nèi)核為其選擇一個臨時的端口號,而服務(wù)器端通過調(diào)用bind 函數(shù)將端口號與相應(yīng)的套接字綁定。進(jìn)程可以把一個特定的 IP 地址捆綁到它的套接字上,但是這個 IP 地址必須屬于其所在主機(jī)的網(wǎng)絡(luò)接口之一。對于 TCP 客戶端,這就為在套接字上發(fā)送的 IP 數(shù)據(jù)報(bào)指派了源 IP 地址。對于 TCP 服務(wù)器端,這就限定該套接字只接收那些目的地為這個 IP 地址的客戶端連接。TCP 客戶端一般不把 IP 地址捆綁到它的套接字上。當(dāng)連接套接字時,內(nèi)核將根據(jù)所用外出網(wǎng)絡(luò)接口來選擇源 IP 地址,而所用外出接口則取決于到達(dá)服務(wù)器端所需的路徑。若 TCP 服務(wù)器端沒有把 IP 地址捆綁到它的套接字上,內(nèi)核就把客戶端發(fā)送的 SYN 的目的 IP 地址作為服務(wù)器端的源 IP 地址。

在地址使用方面有下面一些限制:

在進(jìn)程所運(yùn)行的機(jī)器上,指定的地址必須有效,不能指定其他機(jī)器的地址;

地址必須和創(chuàng)建套接字時的地址族所支持的格式相匹配;

端口號必須不小于1024,除非該進(jìn)程具有相應(yīng)的特權(quán)(超級用戶);

一般只有套接字端點(diǎn)能夠與地址綁定,盡管有些協(xié)議允許多重綁定;

listen 函數(shù)

在編寫服務(wù)器程序時需要使用監(jiān)聽函數(shù) listen 。服務(wù)器進(jìn)程不知道要與誰連接,因此,它不會主動地要求與某個進(jìn)程連接,只是一直監(jiān)聽是否有其他客戶進(jìn)程與之連接,然后響應(yīng)該連接請求,并對它做出處理,一個服務(wù)進(jìn)程可以同時處理多個客戶進(jìn)程的連接。listen 函數(shù)描述如下:

  1. /*  
  2.  * 函數(shù)功能:接收連接請求;  
  3.  * 函數(shù)原型:  
  4.  */   
  5. #include <sys/socket.h>   
  6.    
  7. int listen(int sockfd, int backlog);//若成功則返回0,若出錯則返回-1;   
  8. /*  
  9.  * sockfd是套接字描述符;  
  10.  * backlog是該進(jìn)程所要入隊(duì)請求的最大請求數(shù)量;  
  11.  */   

listen 函數(shù)僅由 TCP 服務(wù)器調(diào)用,它有以下兩種作用:

當(dāng) socket 函數(shù)創(chuàng)建一個套接字時,若它被假設(shè)為一個主動套接字,即它是一個將調(diào)用connect 發(fā)起連接的客戶端套接字。listen 函數(shù)把一個未連接的套接字轉(zhuǎn)換成一個被動套接字,指示內(nèi)核應(yīng)該接受指向該套接字的連接請求;

listen 函數(shù)的第二個參數(shù)規(guī)定內(nèi)核應(yīng)該為相應(yīng)套接字排隊(duì)的最大連接個數(shù);

listen 函數(shù)一般應(yīng)該在調(diào)用socket 和bind 這兩個函數(shù)之后,并在調(diào)用 accept 函數(shù)之前調(diào)用。 內(nèi)核為任何一個給定監(jiān)聽套接字維護(hù)兩個隊(duì)列:

未完成連接隊(duì)列,每個這樣的 SYN 報(bào)文段對應(yīng)其中一項(xiàng):已由某個客戶端發(fā)出并到達(dá)服務(wù)器,而服務(wù)器正在等待完成相應(yīng)的 TCP 三次握手過程。這些套接字處于 SYN_REVD 狀態(tài);

已完成連接隊(duì)列,每個已完成 TCP 三次握手過程的客戶端對應(yīng)其中一項(xiàng)。這些套接字處于 ESTABLISHED 狀態(tài);

accept 函數(shù)

accept 函數(shù)由 TCP 服務(wù)器調(diào)用,用于從已完成連接隊(duì)列隊(duì)頭返回下一個已完成連接。如果已完成連接隊(duì)列為空,那么進(jìn)程被投入睡眠。該函數(shù)的返回值是一個新的套接字描述符,返回值是表示已連接的套接字描述符,而第一個參數(shù)是服務(wù)器監(jiān)聽套接字描述符。一個服務(wù)器通常僅僅創(chuàng)建一個監(jiān)聽套接字,它在該服務(wù)器的生命周期內(nèi)一直存在。內(nèi)核為每個由服務(wù)器進(jìn)程接受的客戶連接創(chuàng)建一個已連接套接字(表示 TCP 三次握手已完成),當(dāng)服務(wù)器完成對某個給定客戶的服務(wù)時,相應(yīng)的已連接套接字就會被關(guān)閉。該函數(shù)描述如下:

  1. /* 函數(shù)功能:從已完成連接隊(duì)列隊(duì)頭返回下一個已完成連接;若已完成連接隊(duì)列為空,則進(jìn)程進(jìn)入睡眠;  
  2.  * 函數(shù)原型:  
  3.  */   
  4. int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);//返回值:若成功返回套接字描述符,出錯返回-1;   
  5. /*  
  6.  * 說明:  
  7.  * 參數(shù) cliaddr 和 addrlen 用來返回已連接的對端(客戶端)的協(xié)議地址;  
  8.  *  
  9.  * 該函數(shù)返回套接字描述符,該描述符連接到調(diào)用connect函數(shù)的客戶端;  
  10.  * 這個新的套接字描述符和原始的套接字描述符sockfd具有相同的套接字類型和地址族,而傳給accept函數(shù)的套接字描述符sockfd沒有關(guān)聯(lián)到這個鏈接,  
  11.  * 而是繼續(xù)保持可用狀態(tài)并接受其他連接請求;  
  12.  * 若不關(guān)心客戶端協(xié)議地址,可將cliaddr和addrlen參數(shù)設(shè)置為NULL,否則,在調(diào)用accept之前,應(yīng)將參數(shù)cliaddr設(shè)為足夠大的緩沖區(qū)來存放地址,  
  13.  * 并且將addrlen設(shè)為指向代表這個緩沖區(qū)大小的整數(shù)指針;  
  14.  * accept函數(shù)返回時,會在緩沖區(qū)填充客戶端的地址并更新addrlen所指向的整數(shù)為該地址的實(shí)際大小;  
  15.  *  
  16.  * 若沒有連接請求等待處理,accept會阻塞直到一個請求到來;  

#p#fork 和 exec 函數(shù)

  1. /* 函數(shù)功能:創(chuàng)建子進(jìn)程;  
  2.  * 返回值:  
  3.  * (1)在子進(jìn)程中,返回0;  
  4.  * (2)在父進(jìn)程中,返回新創(chuàng)建子進(jìn)程的進(jìn)程ID;  
  5.  * (3)若出錯,則范回-1;  
  6.  * 函數(shù)原型:  
  7.  */   
  8. #include <unistd.h>   
  9. pid_t fork(void);   
  10. /* 說明:  
  11.  * 該函數(shù)調(diào)用一次若成功則返回兩個值:  
  12.  * 在調(diào)用進(jìn)程(即父進(jìn)程)中,返回新創(chuàng)建進(jìn)程(即子進(jìn)程)的進(jìn)程ID;  
  13.  * 在子進(jìn)程返回值是0;  
  14.  * 因此,可以根據(jù)返回值判斷進(jìn)程是子進(jìn)程還是父進(jìn)程;  
  15.  */   
  16.    
  17. /* exec 序列函數(shù) */   
  18.    
  19. /*  
  20.  * 函數(shù)功能:把當(dāng)前進(jìn)程替換為一個新的進(jìn)程,新進(jìn)程與原進(jìn)程ID相同;  
  21.  * 返回值:若出錯則返回-1,若成功則不返回;  
  22.  * 函數(shù)原型:  
  23.  */   
  24. #include <unistd.h>   
  25. int execl(const char *pathname, const char *arg, ...);   
  26. int execv(const char *pathnam, char *const argv[]);   
  27. int execle(const char *pathname, const char *arg, ... , char *const envp[]);   
  28. int execve(const char *pathnam, char *const argv[], char *const envp[]);   
  29. int execlp(const char *filename, const char *arg, ...);   
  30. int execvp(const char *filename, char *const argv[]);   
  31. /*  6 個函數(shù)的區(qū)別如下:  
  32.  * (1)待執(zhí)行的程序文件是 文件名 還是由 路徑名 指定;  
  33.  * (2)新程序的參數(shù)是 一一列出 還是由一個 指針數(shù)組 來引用;  
  34.  * (3)把調(diào)用進(jìn)程的環(huán)境傳遞給新程序 還是 給新程序指定新的環(huán)境;  
  35.  */   

exec 6個函數(shù)在函數(shù)名和使用語法的規(guī)則上都有細(xì)微的區(qū)別,下面就從可執(zhí)行文件查找方式、參數(shù)傳遞方式及環(huán)境變量這幾個方面進(jìn)行比較。

查找方式:前4個函數(shù)的查找方式都是完整的文件目錄路徑 pathname ,而最后兩個函數(shù)(也就是以p結(jié)尾的兩個函數(shù))可以只給出文件名 filename,系統(tǒng)就會自動按照環(huán)境變量 “$PATH” 所指定的路徑進(jìn)行查找。

參數(shù)傳遞方式:exec 序列函數(shù)的參數(shù)傳遞有兩種方式:一種是逐個列舉的方式,而另一種則是將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞。在這里是以函數(shù)名的第5位字母來區(qū)分的,字母為 “l”(list)的表示逐個列舉參數(shù)的方式,其語法為 const char *arg;字母為 “v”(vertor)的表示將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞,其語法為 char *const argv[]。讀者可以觀察 execl()、execle()、execlp() 的語法與 execv()、execve()、execvp() 的區(qū)別。這里的參數(shù)實(shí)際上就是用戶在使用這個可執(zhí)行文件時所需的全部命令選項(xiàng)字符串(包括該可執(zhí)行程序命令本身)。要注意的是,這些參數(shù)必須以NULL結(jié)束。

環(huán)境變量:exec 序列函數(shù)可以默認(rèn)系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量。這里以 “e”(environment)結(jié)尾的兩個函數(shù) execle() 和 execve() 就可以在 envp[] 中指定當(dāng)前進(jìn)程所使用的環(huán)境變量。

  1.  表 1 exec 序列函數(shù)的總結(jié)       
  2. 前4位 統(tǒng)一為:exec       
  3. 第5位 l:參數(shù)傳遞為逐個列舉方式    execl、execle、execlp   
  4.      v:參數(shù)傳遞為構(gòu)造指針數(shù)組方式     execv、execve、execvp   
  5. 第6位 e:可傳遞新進(jìn)程環(huán)境變量     execle、execve   
  6.      p:可執(zhí)行文件查找方式為文件名     execlp、execvp   

其關(guān)系如下圖:

 

 

并發(fā)服務(wù)器

當(dāng)要求一個服務(wù)器同時為多個客戶服務(wù)時,需要并發(fā)服務(wù)器。TCP 并發(fā)服務(wù)器,它們?yōu)槊總€待處理的客戶端連接調(diào)用 fork 函數(shù)派生一個子進(jìn)程。當(dāng)一個連接建立時,accept 返回,服務(wù)器接著調(diào)用 fork 函數(shù),然后由子進(jìn)程服務(wù)客戶端,父進(jìn)程則等待另一個連接,此時,父進(jìn)程必須關(guān)閉已連接套接字。

close 和 shutdown 函數(shù)

當(dāng)要關(guān)閉套接字時,可使用 close 和 shutdown 函數(shù),其描述如下:

  1. /* 函數(shù)功能:關(guān)閉套接字,若是在 TCP 協(xié)議中,并終止 TCP 連接;  
  2.  * 返回值:若成功則返回0,若出錯則返回-1;  
  3.  * 函數(shù)原型:  
  4.  */   
  5. #include <unistd.h>   
  6. int close(int sockfd);   
  7.    
  8. /*  
  9.  * 函數(shù)功能:關(guān)閉套接字上的輸入或輸出;  
  10.  * 返回值:若成功則返回0,若出錯返回-1;  
  11.  * 函數(shù)原型:  
  12.  */   
  13. #include <sys/socket.h>   
  14. int shutdown(int sockfd, int how);   
  15. /*  
  16.  * 說明:  
  17.  * sockfd表示待操作的套接字描述符;  
  18.  * how表示具體操作,取值如下:  
  19.  * (1)SHUT_RD     關(guān)閉讀端,即不能接收數(shù)據(jù)  
  20.  * (2)SHUT_WR     關(guān)閉寫端,即不能發(fā)送數(shù)據(jù)  
  21.  * (3)SHUT_RDWR   關(guān)閉讀、寫端,即不能發(fā)送和接收數(shù)據(jù)  
  22.  *  
  23.  */   

getsockname 和 getpeername 函數(shù)

為了獲取已綁定到套接字的地址,我們可以調(diào)用函數(shù) getsockname 來實(shí)現(xiàn):

  1. /*  
  2.  * 函數(shù)功能:獲取已綁定到一個套接字的地址;  
  3.  * 返回值:若成功則返回0,若出錯則返回-1;  
  4.  * 函數(shù)原型:  
  5.  */   
  6. #include <sys/socket.h>   
  7.    
  8. int getsockname(int sockfd, struct sockaddr *addr, socklen_t *alenp);   
  9. /*  
  10.  * 說明:  
  11.  * 調(diào)用該函數(shù)之前,設(shè)置alenp為一個指向整數(shù)的指針,該整數(shù)指定緩沖區(qū)sockaddr的大??;  
  12.  * 返回時,該整數(shù)會被設(shè)置成返回地址的大小,如果該地址和提供的緩沖區(qū)長度不匹配,則將其截?cái)喽粓?bào)錯;  
  13.  */   
  14. /*  
  15.  * 函數(shù)功能:獲取套接字對方連接的地址;  
  16.  * 返回值:若成功則返回0,若出錯則返回-1;  
  17.  * 函數(shù)原型:  
  18.  */   
  19. #include <sys/socket.h>   
  20.    
  21. int getpeername(int sockfd, struct sockaddr *addr, socklen_t *alenp);   
  22. /*  
  23.  * 說明:  
  24.  * 該函數(shù)除了返回對方的地址之外,其他功能和getsockname一樣;  
  25.  */   
責(zé)任編輯:林琳 來源: CSDN博客
相關(guān)推薦

2014-12-17 09:22:10

網(wǎng)絡(luò)·安全技術(shù)周刊

2014-12-15 09:28:54

UDP

2015-05-28 10:47:38

Unix網(wǎng)絡(luò)編程TCP

2012-01-06 13:58:47

JavaTCP

2015-10-16 09:33:26

TCPIP網(wǎng)絡(luò)協(xié)議

2015-03-31 11:24:02

2009-03-10 13:59:41

C#套接字編程

2021-02-05 15:20:06

網(wǎng)絡(luò)安全套接字命令

2021-03-14 18:22:23

套接字網(wǎng)絡(luò)通信

2020-10-15 19:10:05

LinuxAPI函數(shù)

2013-12-27 13:39:23

Java套接字

2009-08-21 14:47:39

C#網(wǎng)絡(luò)編程

2010-07-06 15:33:10

UDP套接字

2009-08-21 09:20:44

C#異步套接字

2010-06-08 13:32:19

TCP IP協(xié)議基礎(chǔ)

2019-08-26 09:50:15

TCP連接Socket

2017-01-16 09:26:07

2010-06-13 15:16:02

2009-08-26 09:48:48

C#異步套接字

2010-02-26 15:53:35

WCF套接字連接中斷
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號