• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            開(kāi)源之路

            憶往昔, 項(xiàng)羽不過(guò)江. 江東好風(fēng)光! 今振臂一呼,率甲三千, 試問(wèn)天!
            posts - 86, comments - 55, trackbacks - 0, articles - 0
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            1、 引言

            Linux的興起可以說(shuō)是Internet創(chuàng)造的一個(gè)奇跡。Linux作為一個(gè)完全開(kāi)放其原代碼的免費(fèi)的自由軟件,兼容了各種UNIX標(biāo)準(zhǔn)(如POSIX、UNIX System V 和 BSD UNIX 等)的多用戶、多任務(wù)的具有復(fù)雜內(nèi)核的操作系統(tǒng)。在中國(guó),隨著Internet的普及,一批主要以高等院校的學(xué)生和ISP的技術(shù)人員組成的Linux愛(ài)好者隊(duì)伍已經(jīng)蓬勃成長(zhǎng)起來(lái)。越來(lái)越多的編程愛(ài)好者也逐漸酷愛(ài)上這個(gè)優(yōu)秀的自由軟件。本文介紹了Linux下Socket的基本概念和函數(shù)調(diào)用。

            2、 什么是Socket

            Socket(套接字)是通過(guò)標(biāo)準(zhǔn)的UNIX文件描述符和其它程序通訊的一個(gè)方法。每一個(gè)套接字都用一個(gè)半相關(guān)描述:{協(xié)議,本地地址、本地端口}來(lái)表示;一個(gè)完整的套接字則用一個(gè)相關(guān)描述:{協(xié)議,本地地址、本地端口、遠(yuǎn)程地址、遠(yuǎn)程端口},每一個(gè)套接字都有一個(gè)本地的由操作系統(tǒng)分配的唯一的套接字號(hào)。

            3、 Socket的三種類(lèi)型

            (1) 流式Socket(SOCK_STREAM)

            流式套接字提供可靠的、面向連接的通信流;它使用TCP協(xié)議,從而保證了數(shù)據(jù)傳輸?shù)恼_性和順序的。

            (2) 數(shù)據(jù)報(bào)Socket(SOCK_DGRAM)

            數(shù)據(jù)報(bào)套接字定義了一種無(wú)連接的服務(wù),數(shù)據(jù)通過(guò)相互獨(dú)立的報(bào)文進(jìn)行傳輸,是無(wú)序的,并且不保證可靠、無(wú)差錯(cuò)。它使用數(shù)據(jù)報(bào)協(xié)議UDP

            (3) 原始Socket

            原始套接字允許對(duì)底層協(xié)議如IP或ICMP直接訪問(wèn),它功能強(qiáng)大但使用較為不便,主要用于一些協(xié)議的開(kāi)發(fā)。

            4、 利用套接字發(fā)送數(shù)據(jù)

            1、 對(duì)于流式套接字用系統(tǒng)調(diào)用send()來(lái)發(fā)送數(shù)據(jù)。

            2、 對(duì)于數(shù)據(jù)報(bào)套接字,則需要自己先加一個(gè)信息頭,然后調(diào)用sendto()函數(shù)把數(shù)據(jù)發(fā)送出去。

            5、 Linux中Socket的數(shù)據(jù)結(jié)構(gòu)

            (1) struct sockaddr { //用于存儲(chǔ)套接字地址

            unsigned short sa_family;//地址類(lèi)型

            char sa_data[14]; //14字節(jié)的協(xié)議地址

            };

            (2) struct sockaddr_in{ //in 代表internet

            short int sin_family; //internet協(xié)議族

            unsigned short int sin_port;//端口號(hào),必須是網(wǎng)絡(luò)字節(jié)順序

            struct in_addr sin_addr;//internet地址,必須是網(wǎng)絡(luò)字節(jié)順序

            unsigned char sin_zero;//添0(和struct sockaddr一樣大小

            };

            (3) struct in_addr{

            unsigned long s_addr;

            };

            6、 網(wǎng)絡(luò)字節(jié)順序及其轉(zhuǎn)換函數(shù)

            (1) 網(wǎng)絡(luò)字節(jié)順序

            每一臺(tái)機(jī)器內(nèi)部對(duì)變量的字節(jié)存儲(chǔ)順序不同,而網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)是一定要統(tǒng)一順序的。所以對(duì)內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序不同的機(jī)器,一定要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換,從程序的可移植性要求來(lái)講,就算本機(jī)的內(nèi)部字節(jié)表示順序與網(wǎng)絡(luò)字節(jié)順序相同也應(yīng)該在傳輸數(shù)據(jù)以前先調(diào)用數(shù)據(jù)轉(zhuǎn)換函數(shù),以便程序移植到其它機(jī)器上后能正確執(zhí)行。真正轉(zhuǎn)換還是不轉(zhuǎn)換是由系統(tǒng)函數(shù)自己來(lái)決定的。

            (2) 有關(guān)的轉(zhuǎn)換函數(shù)

            * unsigned short int htons(unsigned short int hostshort):

            主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes

            * unsigned long int htonl(unsigned long int hostlong):

            主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes

            * unsigned short int ntohs(unsigned short int netshort):

            網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)短型進(jìn)行操作4bytes

            * unsigned long int ntohl(unsigned long int netlong):

            網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換成主機(jī)字節(jié)順序,對(duì)無(wú)符號(hào)長(zhǎng)型進(jìn)行操作8bytes

            注:以上函數(shù)原型定義在netinet/in.h里

            7、 IP地址轉(zhuǎn)換

            有三個(gè)函數(shù)將數(shù)字點(diǎn)形式表示的字符串IP地址與32位網(wǎng)絡(luò)字節(jié)順序的二進(jìn)制形式的IP地址進(jìn)行轉(zhuǎn)換

            (1) unsigned long int inet_addr(const char * cp):該函數(shù)把一個(gè)用數(shù)字和點(diǎn)表示的IP地址的字符串轉(zhuǎn)換成一個(gè)無(wú)符號(hào)長(zhǎng)整型,如:struct sockaddr_in ina

            ina.sin_addr.s_addr=inet_addr("202.206.17.101")

            該函數(shù)成功時(shí):返回轉(zhuǎn)換結(jié)果;失敗時(shí)返回常量INADDR_NONE,該常量=-1,二進(jìn)制的無(wú)符號(hào)整數(shù)-1相當(dāng)于255.255.255.255,這是一個(gè)廣播地址,所以在程序中調(diào)用iner_addr()時(shí),一定要人為地對(duì)調(diào)用失敗進(jìn)行處理。由于該函數(shù)不能處理廣播地址,所以在程序中應(yīng)該使用函數(shù)inet_aton()。

            (2)int inet_aton(const char * cp,struct in_addr * inp):此函數(shù)將字符串形式的IP地址轉(zhuǎn)換成二進(jìn)制形式的IP地址;成功時(shí)返回1,否則返回0,轉(zhuǎn)換后的IP地址存儲(chǔ)在參數(shù)inp中。

            (3) char * inet_ntoa(struct in-addr in):將32位二進(jìn)制形式的IP地址轉(zhuǎn)換為數(shù)字點(diǎn)形式的IP地址,結(jié)果在函數(shù)返回值中返回,返回的是一個(gè)指向字符串的指針。

            8、 字節(jié)處理函數(shù)

            Socket地址是多字節(jié)數(shù)據(jù),不是以空字符結(jié)尾的,這和C語(yǔ)言中的字符串是不同的。Linux提供了兩組函數(shù)來(lái)處理多字節(jié)數(shù)據(jù),一組以b(byte)開(kāi)頭,是和BSD系統(tǒng)兼容的函數(shù),另一組以mem(內(nèi)存)開(kāi)頭,是ANSI C提供的函數(shù)。

            以b開(kāi)頭的函數(shù)有:

            (1) void bzero(void * s,int n):將參數(shù)s指定的內(nèi)存的前n個(gè)字節(jié)設(shè)置為0,通常它用來(lái)將套接字地址清0。

            (2) void bcopy(const void * src,void * dest,int n):從參數(shù)src指定的內(nèi)存區(qū)域拷貝指定數(shù)目的字節(jié)內(nèi)容到參數(shù)dest指定的內(nèi)存區(qū)域。

            (3) int bcmp(const void * s1,const void * s2,int n):比較參數(shù)s1指定的內(nèi)存區(qū)域和參數(shù)s2指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。

            注:以上函數(shù)的原型定義在strings.h中。

            以mem開(kāi)頭的函數(shù)有:

            (1) void * memset(void * s,int c,size_t n):將參數(shù)s指定的內(nèi)存區(qū)域的前n個(gè)字節(jié)設(shè)置為參數(shù)c的內(nèi)容。

            (2) void * memcpy(void * dest,const void * src,size_t n):功能同bcopy(),區(qū)別:函數(shù)bcopy()能處理參數(shù)src和參數(shù)dest所指定的區(qū)域有重疊的情況,memcpy()則不能。

            (4) int memcmp(const void * s1,const void * s2,size_t n):比較參數(shù)s1和參數(shù)s2指定區(qū)域的前n個(gè)字節(jié)內(nèi)容,如果相同則返回0,否則返回非0。

            注:以上函數(shù)的原型定義在string.h中。

            9、 基本套接字函數(shù)

            (1) socket()

            #include<sys/types.h>

            #include<sys/socket.h>

            int socket(int domain,int type,int protocol)

            參數(shù)domain指定要?jiǎng)?chuàng)建的套接字的協(xié)議族,可以是如下值:

            AF_UNIX //UNIX域協(xié)議族,本機(jī)的進(jìn)程間通訊時(shí)使用

            AF_INET //Internet協(xié)議族(TCP/IP)

            AF_ISO //ISO協(xié)議族

            參數(shù)type指定套接字類(lèi)型,可以是如下值:

            SOCK_STREAM //流套接字,面向連接的和可靠的通信類(lèi)型

            SOCK_DGRAM //數(shù)據(jù)報(bào)套接字,非面向連接的和不可靠的通信類(lèi)型

            SOCK_RAW //原始套接字,只對(duì)Internet協(xié)議有效,可以用來(lái)直接訪問(wèn)IP協(xié)議

            參數(shù)protocol通常設(shè)置成0,表示使用默認(rèn)協(xié)議,如Internet協(xié)議族的流套接字使用TCP協(xié)議,而數(shù)據(jù)報(bào)套接字使用UDP協(xié)議。當(dāng)套接字是原始套接字類(lèi)型時(shí),需要指定參數(shù)protocol,因?yàn)樵继捉幼謱?duì)多種協(xié)議有效,如ICMP和IGMP等。

            Linux系統(tǒng)中創(chuàng)建一個(gè)套接字的操作主要是:在內(nèi)核中創(chuàng)建一個(gè)套接字?jǐn)?shù)據(jù)結(jié)構(gòu),然后返回一個(gè)套接字描述符標(biāo)識(shí)這個(gè)套接字?jǐn)?shù)據(jù)結(jié)構(gòu)。這個(gè)套接字?jǐn)?shù)據(jù)結(jié)構(gòu)包含連接的各種信息,如對(duì)方地址、TCP狀態(tài)以及發(fā)送和接收緩沖區(qū)等等,TCP協(xié)議根據(jù)這個(gè)套接字?jǐn)?shù)據(jù)結(jié)構(gòu)的內(nèi)容來(lái)控制這條連接。

            (2) 函數(shù)connect()

            #include<sys/types.h>

            #include<sys/socket.h>

            int connect(int sockfd,struct sockaddr * servaddr,int addrlen)

            參數(shù)sockfd是函數(shù)socket返回的套接字描述符;參數(shù)servaddr指定遠(yuǎn)程服務(wù)器的套接字地址,包括服務(wù)器的IP地址和端口號(hào);參數(shù)addrlen指定這個(gè)套接字地址的長(zhǎng)度。成功時(shí)返回0,否則返回-1,并設(shè)置全局變量為以下任何一種錯(cuò)誤類(lèi)型:ETIMEOUT、ECONNREFUSED、EHOSTUNREACH或ENETUNREACH。

            在調(diào)用函數(shù)connect之前,客戶機(jī)需要指定服務(wù)器進(jìn)程的套接字地址。客戶機(jī)一般不需要指定自己的套接字地址(IP地址和端口號(hào)),系統(tǒng)會(huì)自動(dòng)從1024至5000的端口號(hào)范圍內(nèi)為它選擇一個(gè)未用的端口號(hào),然后以這個(gè)端口號(hào)和本機(jī)的IP地址填充這個(gè)套接字地址。

            客戶機(jī)調(diào)用函數(shù)connect來(lái)主動(dòng)建立連接。這個(gè)函數(shù)將啟動(dòng)TCP協(xié)議的3次握手過(guò)程。在建立連接之后或發(fā)生錯(cuò)誤時(shí)函數(shù)返回。連接過(guò)程可能出現(xiàn)的錯(cuò)誤情況有:

            (1) 如果客戶機(jī)TCP協(xié)議沒(méi)有接收到對(duì)它的SYN數(shù)據(jù)段的確認(rèn),函數(shù)以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為ETIMEOUT。通常TCP協(xié)議在發(fā)送SYN數(shù)據(jù)段失敗之后,會(huì)多次發(fā)送SYN數(shù)據(jù)段,在所有的發(fā)送都高中失敗之后,函數(shù)以錯(cuò)誤返回。

            注:SYN(synchronize)位:請(qǐng)求連接。TCP用這種數(shù)據(jù)段向?qū)Ψ絋CP協(xié)議請(qǐng)求建立連接。在這個(gè)數(shù)據(jù)段中,TCP協(xié)議將它選擇的初始序列號(hào)通知對(duì)方,并且與對(duì)方協(xié)議協(xié)商最大數(shù)據(jù)段大小。SYN數(shù)據(jù)段的序列號(hào)為初始序列號(hào),這個(gè)SYN數(shù)據(jù)段能夠被確認(rèn)。當(dāng)協(xié)議接收到對(duì)這個(gè)數(shù)據(jù)段的確認(rèn)之后,建立TCP連接。

            (2) 如果遠(yuǎn)程TCP協(xié)議返回一個(gè)RST數(shù)據(jù)段,函數(shù)立即以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為ECONNREFUSED。當(dāng)遠(yuǎn)程機(jī)器在SYN數(shù)據(jù)段指定的目的端口號(hào)處沒(méi)有服務(wù)進(jìn)程在等待連接時(shí),遠(yuǎn)程機(jī)器的TCP協(xié)議將發(fā)送一個(gè)RST數(shù)據(jù)段,向客戶機(jī)報(bào)告這個(gè)錯(cuò)誤。客戶機(jī)的TCP協(xié)議在接收到RST數(shù)據(jù)段后不再繼續(xù)發(fā)送SYN數(shù)據(jù)段,函數(shù)立即以錯(cuò)誤返回。

            注:RST(reset)位:表示請(qǐng)求重置連接。當(dāng)TCP協(xié)議接收到一個(gè)不能處理的數(shù)據(jù)段時(shí),向?qū)Ψ絋CP協(xié)議發(fā)送這種數(shù)據(jù)段,表示這個(gè)數(shù)據(jù)段所標(biāo)識(shí)的連接出現(xiàn)了某種錯(cuò)誤,請(qǐng)求TCP協(xié)議將這個(gè)連接清除。有3種情況可能導(dǎo)致TCP協(xié)議發(fā)送RST數(shù)據(jù)段:(1)SYN數(shù)據(jù)段指定的目的端口處沒(méi)有接收進(jìn)程在等待;(2)TCP協(xié)議想放棄一個(gè)已經(jīng)存在的連接;(3)TCP接收到一個(gè)數(shù)據(jù)段,但是這個(gè)數(shù)據(jù)段所標(biāo)識(shí)的連接不存在。接收到RST數(shù)據(jù)段的TCP協(xié)議立即將這條連接非正常地?cái)嚅_(kāi),并向應(yīng)用程序報(bào)告錯(cuò)誤。

            (3) 如果客戶機(jī)的SYN數(shù)據(jù)段導(dǎo)致某個(gè)路由器產(chǎn)生“目的地不可到達(dá)”類(lèi)型的ICMP消息,函數(shù)以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為EHOSTUNREACH或ENETUNREACH。通常TCP協(xié)議在接收到這個(gè)ICMP消息之后,記錄這個(gè)消息,然后繼續(xù)幾次發(fā)送SYN數(shù)據(jù)段,在所有的發(fā)送都告失敗之后,TCP協(xié)議檢查這個(gè)ICMP消息,函數(shù)以錯(cuò)誤返回。

            注:ICMP:Internet 消息控制協(xié)議。Internet的運(yùn)行主要是由Internet的路由器來(lái)控制,路由器完成IP數(shù)據(jù)包的發(fā)送和接收,如果發(fā)送數(shù)據(jù)包時(shí)發(fā)生錯(cuò)誤,路由器使用ICMP協(xié)議來(lái)報(bào)告這些錯(cuò)誤。ICMP數(shù)據(jù)包是封裝在IP數(shù)據(jù)包的數(shù)據(jù)部分中進(jìn)行傳輸?shù)模涓袷饺缦拢?

            類(lèi)型

            校驗(yàn)和

            數(shù)據(jù)

            0 8 16 24 31

            類(lèi)型:指出ICMP數(shù)據(jù)包的類(lèi)型。

            代碼:提供ICMP數(shù)據(jù)包的進(jìn)一步信息。

            校驗(yàn)和:提供了對(duì)整個(gè)ICMP數(shù)據(jù)包內(nèi)容的校驗(yàn)和。

            ICMP數(shù)據(jù)包主要有以下類(lèi)型:

            (1) 目的地不可到達(dá):A、目的主機(jī)未運(yùn)行;B、目的地址不存在;C、路由表中沒(méi)有目的地址對(duì)應(yīng)的條目,因而路由器無(wú)法找到去往目的主機(jī)的路由。

            (2) 超時(shí):路由器將接收到的IP數(shù)據(jù)包的生存時(shí)間(TTL)域減1,如果這個(gè)域的值變?yōu)?,路由器丟棄這個(gè)IP數(shù)據(jù)包,并且發(fā)送這種ICMP消息。

            (3) 參數(shù)出錯(cuò):當(dāng)IP數(shù)據(jù)包中有無(wú)效域時(shí)發(fā)送。

            (4) 重定向:將一條新的路徑通知主機(jī)。

            (5) ECHO請(qǐng)求、ECHO回答:這兩條消息用語(yǔ)測(cè)試目的主機(jī)是否可以到達(dá)。請(qǐng)求者向目的主機(jī)發(fā)送ECHO請(qǐng)求ICMP數(shù)據(jù)包,目的主機(jī)在接收到這個(gè)ICMP數(shù)據(jù)包之后,返回ECHO回答ICMP數(shù)據(jù)包。

            (6) 時(shí)戳請(qǐng)求、時(shí)戳回答:ICMP協(xié)議使用這兩種消息從其他機(jī)器處獲得其時(shí)鐘的當(dāng)前時(shí)間。

            調(diào)用函數(shù)connect的過(guò)程中,當(dāng)客戶機(jī)TCP協(xié)議發(fā)送了SYN數(shù)據(jù)段的確認(rèn)之后,TCP狀態(tài)由CLOSED狀態(tài)轉(zhuǎn)為SYN_SENT狀態(tài),在接收到對(duì)SYN數(shù)據(jù)段的確認(rèn)之后,TCP狀態(tài)轉(zhuǎn)換成ESTABLISHED狀態(tài),函數(shù)成功返回。如果調(diào)用函數(shù)connect失敗,應(yīng)該用close關(guān)閉這個(gè)套接字描述符,不能再次使用這個(gè)套接字描述符來(lái)調(diào)用函數(shù)connect。

            注:TCP協(xié)議狀態(tài)轉(zhuǎn)換圖:

            被動(dòng)OPEN CLOSE 主動(dòng)OPEN

            (建立TCB) (刪除TCB) (建立TCB,

            發(fā)送SYN)

            接收SYN SEND

            (發(fā)送SYN,ACK) (發(fā)送SYN)

            接收SYN的ACK(無(wú)動(dòng)作)

            接收SYN的ACK 接收SYN,ACK

            (無(wú)動(dòng)作) (發(fā)送ACK)

            CLOSE

            (發(fā)送FIN) CLOSE 接收FIN

            (發(fā)送FIN) (發(fā)送FIN)

            接收FIN

            接收FIN的ACK(無(wú)動(dòng)作) (發(fā)送ACK) CLOSE(發(fā)送FIN)

            接收FIN 接收FIN的ACK 接收FIN的ACK

            (發(fā)送ACK) (無(wú)動(dòng)作) (無(wú)動(dòng)作)

            2MSL超時(shí)(刪除TCB)

            (3) 函數(shù)bind()

            函數(shù)bind將本地地址與套接字綁定在一起,其定義如下:

            #include<sys/types.h>

            #include<sys/socket.h>

            int bind(int sockfd,struct sockaddr * myaddr,int addrlen);

            參數(shù)sockfd是函數(shù)sockt返回的套接字描述符;參數(shù)myaddr是本地地址;參數(shù)addrlen是套接字地址結(jié)構(gòu)的長(zhǎng)度。執(zhí)行成功時(shí)返回0,否則,返回-1,并設(shè)置全局變量errno為錯(cuò)誤類(lèi)型EADDRINUSER。

            服務(wù)器和客戶機(jī)都可以調(diào)用函數(shù)bind來(lái)綁定套接字地址,但一般是服務(wù)器調(diào)用函數(shù)bind來(lái)綁定自己的公認(rèn)端口號(hào)。綁定操作一般有如下幾種組合方式:

            表1

            程序類(lèi)型

            IP地址

            端口號(hào)

            說(shuō)明

            服務(wù)器

            INADDR_ANY

            非零值

            指定服務(wù)器的公認(rèn)端口號(hào)

            服務(wù)器

            本地IP地址

            非零值

            指定服務(wù)器的IP地址和公認(rèn)端口號(hào)

            客戶機(jī)

            INADDR_ANY

            非零值

            指定客戶機(jī)的連接端口號(hào)

            客戶機(jī)

            本地IP地址

            非零值

            指定客戶機(jī)的IP地址連接端口號(hào)

            客戶機(jī)

            本地IP地址

            指定客戶機(jī)的IP地址

            分別說(shuō)明如下:

            (1) 服務(wù)器指定套接字地址的公認(rèn)端口號(hào),不指定IP地址:即服務(wù)器調(diào)用bind時(shí),設(shè)置套接字的IP地址為特殊的INADDE-ANY,表示它愿意接收來(lái)自任何網(wǎng)絡(luò)設(shè)備接口的客戶機(jī)連接。這是服務(wù)器最常用的綁定方式。

            (2) 服務(wù)器指定套接字地址的公認(rèn)端口號(hào)和IP地址:服務(wù)器調(diào)用bind時(shí),如果設(shè)置套接字的IP地址為某個(gè)本地IP地址,這表示這臺(tái)機(jī)器只接收來(lái)自對(duì)應(yīng)于這個(gè)IP地址的特定網(wǎng)絡(luò)設(shè)備接口的客戶機(jī)連接。當(dāng)服務(wù)器有多塊網(wǎng)卡時(shí),可以用這種方式來(lái)限制服務(wù)器的接收范圍。

            (3) 客戶機(jī)指定套接字地址的連接端口號(hào):一般情況下,客戶機(jī)調(diào)用connect函數(shù)時(shí)不用指定自己的套接字地址的端口號(hào)。系統(tǒng)會(huì)自動(dòng)為它選擇一個(gè)未用的端口號(hào),并且用本地的IP地址來(lái)填充套接字地址中的相應(yīng)項(xiàng)。但有時(shí)客戶機(jī)需要使用一個(gè)特定的端口號(hào)(比如保留端口號(hào)),而系統(tǒng)不會(huì)未客戶機(jī)自動(dòng)分配一個(gè)保留端口號(hào),所以需要調(diào)用函數(shù)bind來(lái)和一個(gè)未用的保留端口號(hào)綁定。

            (4) 指定客戶機(jī)的IP地址和連接端口號(hào):表示客戶機(jī)使用指定的網(wǎng)絡(luò)設(shè)備接口和端口號(hào)進(jìn)行通信。

            (5) 指定客戶機(jī)的IP地址:表示客戶機(jī)使用指定的網(wǎng)絡(luò)設(shè)備接口和端口號(hào)進(jìn)行通信,系統(tǒng)自動(dòng)為客戶機(jī)選一個(gè)未用的端口號(hào)。一般只有在主機(jī)有多個(gè)網(wǎng)絡(luò)設(shè)備接口時(shí)使用。

            我們一般不在客戶機(jī)上使用固定的客戶機(jī)端口號(hào),除非是必須使用的情況。在客戶機(jī)上使用固定的端口號(hào)有以下不利:

            (1) 服務(wù)器執(zhí)行主動(dòng)關(guān)閉操作:服務(wù)器最后進(jìn)入TIME_WAIT狀態(tài)。當(dāng)客戶機(jī)再次與這個(gè)服務(wù)器進(jìn)行連接時(shí),仍使用相同的客戶機(jī)端口號(hào),于是這個(gè)連接與前次連接的套接字對(duì)完全一樣,但是一呢、為前次連接處于TIME_WAIT狀態(tài),并未消失,所以這次連接請(qǐng)求被拒絕,函connect以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為ECONNREFUSED

            (2) 客戶機(jī)執(zhí)行主動(dòng)關(guān)閉操作:客戶機(jī)最后進(jìn)入TIME_WAIT狀態(tài)。當(dāng)馬上再次執(zhí)行這個(gè)客戶機(jī)程序時(shí),客戶機(jī)將繼續(xù)與這個(gè)固定客戶機(jī)端口號(hào)綁定,但因?yàn)榍按芜B接處于TIME_WAIT狀態(tài),并未消失,系統(tǒng)會(huì)發(fā)現(xiàn)這個(gè)端口號(hào)仍被占用,所以這次綁定操作失敗,函數(shù)bind以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為EADDRINUSE。

            (4) 函數(shù)listen()

            函數(shù)listen將一個(gè)套接字轉(zhuǎn)換為征聽(tīng)套接字,定義如下;

            #include<sys/socket,h>

            int listen(int sockfd,int backlog)

            參數(shù)sockfd指定要轉(zhuǎn)換的套接字描述符;參數(shù)backlog設(shè)置請(qǐng)求隊(duì)列的最大長(zhǎng)度;執(zhí)行成功時(shí)返回0, 否則返回-1。函數(shù)listen功能有兩個(gè):

            (1) 將一個(gè)尚未連接的主動(dòng)套接字(函數(shù)socket創(chuàng)建的可以用來(lái)進(jìn)行主動(dòng)連接但不能接受連接請(qǐng)求的套接字)轉(zhuǎn)換成一個(gè)被動(dòng)連接套接字。執(zhí)行l(wèi)isten之后,服務(wù)器的TCP狀態(tài)由CLOSED轉(zhuǎn)為L(zhǎng)ISTEN狀態(tài)。

            (2) TCP協(xié)議將到達(dá)的連接請(qǐng)求隊(duì)列,函數(shù)listen的第二個(gè)參數(shù)指定這個(gè)隊(duì)列的最大長(zhǎng)度。

            注:參數(shù)backlog的作用:

            TCP協(xié)議為每一個(gè)征聽(tīng)套接字維護(hù)兩個(gè)隊(duì)列:

            (1) 未完成連接隊(duì)列:每個(gè)尚未完成3次握手操作的TCP連接在這個(gè)隊(duì)列中占有一項(xiàng)。TCP希望儀在接收到一個(gè)客戶機(jī)SYN數(shù)據(jù)段之后,在這個(gè)隊(duì)列中創(chuàng)建一個(gè)新條目,然后發(fā)送對(duì)客戶機(jī)SYN數(shù)據(jù)段的確認(rèn)和自己的SYN數(shù)據(jù)段(ACK+SYN數(shù)據(jù)段),等待客戶機(jī)對(duì)自己的SYN數(shù)據(jù)段的確認(rèn)。此時(shí),套接字處于SYN_RCVD狀態(tài)。這個(gè)條目將保存在這個(gè)隊(duì)列中,直到客戶機(jī)返回對(duì)SYN數(shù)據(jù)段的確認(rèn)或者連接超時(shí)。

            (2) 完成連接隊(duì)列:每個(gè)已經(jīng)完成3次握手操作,但尚未被應(yīng)用程序接收(調(diào)用函數(shù)accept)的TCP連接在這個(gè)隊(duì)列中占有一項(xiàng)。當(dāng)一個(gè)在未完成連接隊(duì)列中的連接接收到對(duì)SYN數(shù)據(jù)段的確認(rèn)之后,完成3次握手操作,TCP協(xié)議將它從未完成連接隊(duì)列移到完成連接隊(duì)列中。此時(shí),套接字處于ESTABLISHED狀態(tài)。這個(gè)條目將保存在這個(gè)隊(duì)列中,直到應(yīng)用程序調(diào)用函數(shù)accept來(lái)接收它。

            參數(shù)backlog指定某個(gè)征聽(tīng)套接字的完成連接隊(duì)列的最大長(zhǎng)度,表示這個(gè)套接字能夠接收的最大數(shù)目的未接收連接。如果當(dāng)一個(gè)客戶機(jī)的SYN數(shù)據(jù)段到達(dá)時(shí),征聽(tīng)套接字的完成隊(duì)列已經(jīng)滿了,那么TCP協(xié)議將忽略這個(gè)SYN數(shù)據(jù)段。對(duì)于不能接收的SYN數(shù)據(jù)段,TCP協(xié)議不發(fā)送RST數(shù)據(jù)段,

            (5) 函數(shù)accept()

            函數(shù)accept從征聽(tīng)套接字的完成隊(duì)列中接收一個(gè)已經(jīng)建立起來(lái)的TCP連接。如果完成連接隊(duì)列為空,那么這個(gè)進(jìn)程睡眠。

            #include<sys/socket.h>

            int accept(int sockfd,struct sockaddr * addr,int * addrlen)

            參數(shù)sockfd指定征聽(tīng)套接字描述符;參數(shù)addr為指向一個(gè)Internet套接字地址結(jié)構(gòu)的指針;參數(shù)addrlen為指向一個(gè)整型變量的指針。執(zhí)行成功時(shí),返回3個(gè)結(jié)果:函數(shù)返回值為一個(gè)新的套接字描述符,標(biāo)識(shí)這個(gè)接收的連接;參數(shù)addr指向的結(jié)構(gòu)變量中存儲(chǔ)客戶機(jī)地址;參數(shù)addrlen指向的整型變量中存儲(chǔ)客戶機(jī)地址的長(zhǎng)度。失敗時(shí)返回-1。

            征聽(tīng)套接字專為接收客戶機(jī)連接請(qǐng)求,完成3次握手操作而用的,所以TCP協(xié)議不能使用征聽(tīng)套接字描述符來(lái)標(biāo)識(shí)這個(gè)連接,于是TCP協(xié)議創(chuàng)建一個(gè)新的套接字來(lái)標(biāo)識(shí)這個(gè)要接收的連接,并將它的描述符發(fā)揮給應(yīng)用程序。現(xiàn)在有兩個(gè)套接字,一個(gè)是調(diào)用函數(shù)accept時(shí)使用的征聽(tīng)套接字,另一個(gè)是函數(shù)accept返回的連接套接字(connected socket)。一個(gè)服務(wù)器通常只需創(chuàng)建一個(gè)征聽(tīng)套接字,在服務(wù)器進(jìn)程的整個(gè)活動(dòng)期間,用它來(lái)接收所有客戶機(jī)的連接請(qǐng)求,在服務(wù)器進(jìn)程終止前關(guān)閉這個(gè)征聽(tīng)套接字;對(duì)于沒(méi)一個(gè)接收的(accepted)連接,TCP協(xié)議都創(chuàng)建一個(gè)新的連接套接字來(lái)標(biāo)識(shí)這個(gè)連接,服務(wù)器使用這個(gè)連接套接字與客戶機(jī)進(jìn)行通信操作,當(dāng)服務(wù)器處理完這個(gè)客戶機(jī)請(qǐng)求時(shí),關(guān)閉這個(gè)連接套接字。

            當(dāng)函數(shù)accept阻塞等待已經(jīng)建立的連接時(shí),如果進(jìn)程捕獲到信號(hào),函數(shù)將以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為EINTR。對(duì)于這種錯(cuò)誤,一般重新調(diào)用函數(shù)accept來(lái)接收連接。

            (6) 函數(shù)close()

            函數(shù)close關(guān)閉一個(gè)套接字描述符。定義如下:

            #include<unistd.h>

            int close(int sockfd);

            執(zhí)行成功時(shí)返回0,否則返回-1。與操作文件描述符的close一樣,函數(shù)close將套接字描述符的引用計(jì)數(shù)器減1,如果描述符的引用計(jì)數(shù)大于0,則表示還有進(jìn)程引用這個(gè)描述符,函數(shù)close正常返回;如果為0,則啟動(dòng)清除套接字描述符的操作,函數(shù)close立即正常返回。

            調(diào)用close之后,進(jìn)程將不再能夠訪問(wèn)這個(gè)套接字,但TCP協(xié)議將繼續(xù)使用這個(gè)套接字,將尚未發(fā)送的數(shù)據(jù)傳遞到對(duì)方,然后發(fā)送FIN數(shù)據(jù)段,執(zhí)行關(guān)閉操作,一直等到這個(gè)TCP連接完全關(guān)閉之后,TCP協(xié)議才刪除該套接字。

            (7) 函數(shù)read()和write()

            用于從套接字讀寫(xiě)數(shù)據(jù)。定義如下:

            int read(int fd,char * buf,int len)

            int write(int fd,char * buf,int len)

            函數(shù)執(zhí)行成功時(shí),返回讀或?qū)懙臄?shù)據(jù)量的大小,失敗時(shí)返回-1。

            每個(gè)TCP套接字都有兩個(gè)緩沖區(qū):套接字發(fā)送緩沖區(qū)、套接字接收緩沖區(qū),分別處理發(fā)送和接收任務(wù)。從網(wǎng)絡(luò)讀、寫(xiě)數(shù)據(jù)的操作是由TCP協(xié)議在內(nèi)核中完成的:TCP協(xié)議將從網(wǎng)絡(luò)上接收到的數(shù)據(jù)保存在相應(yīng)套接字的接收緩沖區(qū)中,等待用戶調(diào)用函數(shù)將它們從接收緩沖區(qū)拷貝到用戶緩沖區(qū);用戶將要發(fā)送的數(shù)據(jù)拷貝到相應(yīng)套接字的發(fā)送緩沖區(qū)中,然后由TCP協(xié)議按照一定的算法處理這些數(shù)據(jù)。

            讀寫(xiě)連接套接字的操作與讀寫(xiě)文件的操作類(lèi)似,也可以使用函數(shù)read和write。函數(shù)read完成將數(shù)據(jù)從套接字接收緩沖區(qū)拷貝到用戶緩沖區(qū):當(dāng)套接字接收緩沖區(qū)有數(shù)據(jù)可讀時(shí),1:可讀數(shù)據(jù)量大于函數(shù)read指定值,返回函數(shù)參數(shù)len指定的數(shù)據(jù)量;2:了度數(shù)據(jù)量小于函數(shù)read指定值,函數(shù)read不等待請(qǐng)求的所有數(shù)據(jù)都到達(dá),而是立即返回實(shí)際讀到的數(shù)據(jù)量;當(dāng)無(wú)數(shù)據(jù)可讀時(shí),函數(shù)read將阻塞不返回,等待數(shù)據(jù)到達(dá)。

            當(dāng)TCP協(xié)議接收到FIN數(shù)據(jù)段,相當(dāng)于給讀操作一個(gè)文件結(jié)束符,此時(shí)read函數(shù)返回0,并且以后所有在這個(gè)套接字上的讀操作均返回0,這和普通文件中遇到文件結(jié)束符是一樣的。

            當(dāng)TCP協(xié)議接收到RST數(shù)據(jù)段,表示連接出現(xiàn)了某種錯(cuò)誤,函數(shù)read將以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為ECONNERESET。并且以后所有在這個(gè)套接字上的讀操作均返回錯(cuò)誤。錯(cuò)誤返回時(shí)返回值小于0。

            函數(shù)write完成將數(shù)據(jù)從用戶緩沖區(qū)拷貝到套接字發(fā)送緩沖區(qū)的任務(wù):到套接字發(fā)送緩沖區(qū)有足夠拷貝所有用戶數(shù)據(jù)的空間時(shí),函數(shù)write將數(shù)據(jù)拷貝到這個(gè)緩沖區(qū)中,并返回老輩的數(shù)量大小,如果可用空間小于write參數(shù)len指定的大小時(shí),函數(shù)write將阻塞不返回,等待緩沖區(qū)有足夠的空間。

            當(dāng)TCP協(xié)議接收到RST數(shù)據(jù)段(當(dāng)對(duì)方已經(jīng)關(guān)閉了這條連接之后,繼續(xù)向這個(gè)套接字發(fā)送數(shù)據(jù)將導(dǎo)致對(duì)方TCP協(xié)議返回RST數(shù)據(jù)段),TCP協(xié)議接收到RST數(shù)據(jù)段時(shí),函數(shù)write將以錯(cuò)誤返回,錯(cuò)誤類(lèi)型為EINTR。以后可以繼續(xù)在這個(gè)套接字上寫(xiě)數(shù)據(jù)。

            (8) 函數(shù)getsockname()和getpeername()

            函數(shù)getsockname返回套接字的本地地址;函數(shù)getpeername返回套接字對(duì)應(yīng)的遠(yuǎn)程地址。

            10、 結(jié)束語(yǔ)

            網(wǎng)絡(luò)程序設(shè)計(jì)全靠套接字接收和發(fā)送信息。上文主要講述了Linux 下Socket的基本概念、Sockets API以及Socket所涉及到的TCP常識(shí)。


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            青青青国产精品国产精品久久久久| 99久久www免费人成精品| 精品国产婷婷久久久| 久久91精品久久91综合| 久久天堂AV综合合色蜜桃网| 久久久久亚洲精品日久生情 | 99久久香蕉国产线看观香 | 人妻精品久久久久中文字幕69 | 免费一级欧美大片久久网| 久久九九全国免费| 99国内精品久久久久久久| 亚洲综合精品香蕉久久网97| 99久久99久久精品国产片| 久久久久无码国产精品不卡| 亚洲人成无码久久电影网站| 亚洲欧美日韩精品久久亚洲区| 久久乐国产综合亚洲精品| 99久久这里只精品国产免费| 色诱久久久久综合网ywww| 久久久精品人妻一区二区三区四| 99久久综合狠狠综合久久止| 国产精品一区二区久久精品无码 | 青青草原精品99久久精品66| 成人资源影音先锋久久资源网| 久久精品国产亚洲网站| 久久精品国产福利国产琪琪| 久久久久久久91精品免费观看| 亚洲va久久久噜噜噜久久天堂| 国产精品久久久久久福利漫画 | 久久精品国产色蜜蜜麻豆| 久久超乳爆乳中文字幕| 久久久久亚洲AV综合波多野结衣| 伊人久久综合无码成人网| 欧美激情精品久久久久| 久久九九久精品国产免费直播| 国产亚洲精品美女久久久| 久久婷婷五月综合成人D啪| 久久人人爽爽爽人久久久| 久久久久国色AV免费看图片 | 久久精品免费观看| 狠狠色丁香久久婷婷综合_中|