第二部分基本套接口編程_第1頁(yè)
第二部分基本套接口編程_第2頁(yè)
第二部分基本套接口編程_第3頁(yè)
第二部分基本套接口編程_第4頁(yè)
第二部分基本套接口編程_第5頁(yè)
已閱讀5頁(yè),還剩87頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、基本套接字編程 主要內(nèi)容主要內(nèi)容n套接字基礎(chǔ)n套接字地址結(jié)構(gòu)n套接字基本函數(shù)n簡(jiǎn)單TCP套接字編程n簡(jiǎn)單UDP套接字編程 套接字基礎(chǔ)套接字基礎(chǔ)n網(wǎng)絡(luò)編程接口有兩個(gè)發(fā)展方向:Socket,TLIn套接字是一種網(wǎng)絡(luò)API,程序員可以用之開(kāi)發(fā)網(wǎng)絡(luò)程序。套接字接口本意在于提供一種進(jìn)程間通信的方法,使得在相同或不同的主機(jī)上的進(jìn)程能以相同的規(guī)范進(jìn)行雙向信息傳送。進(jìn)程1進(jìn)程2網(wǎng)絡(luò)編程接口(socket)網(wǎng)絡(luò)通信協(xié)議服務(wù)接口(TCP/IP)底層通信協(xié)議和網(wǎng)絡(luò)介質(zhì)套接字類(lèi)型套接字類(lèi)型n套接字支持多種通信協(xié)議:nUnix:Unix系統(tǒng)內(nèi)部協(xié)議nINET:IP版本4nINET6:IP版本6nLinux支持多種套接字

2、類(lèi)型,即應(yīng)用程序希望的通信服務(wù)類(lèi)型nSOCKET_STREAM:雙向可靠數(shù)據(jù)流,對(duì)應(yīng)TCPnSOCKET_DGRAM:雙向不可靠數(shù)據(jù)報(bào),對(duì)應(yīng)UDPnSOCKET_RAW:是低于傳輸層的低級(jí)協(xié)議或物理網(wǎng)絡(luò)提供的套接字類(lèi)型,可以訪問(wèn)內(nèi)部網(wǎng)絡(luò)接口。例如接收和發(fā)送ICMP報(bào)套接字地址結(jié)構(gòu)(IPv4)n大多數(shù)套接字函數(shù)需要一個(gè)指向套接字地址結(jié)構(gòu)的參數(shù),每個(gè)協(xié)議族都定義它自己的套接字地址結(jié)構(gòu),一般以”sockaddr_”開(kāi)頭,并以協(xié)議簇為后綴。(netinet/in.h)typedef uint32_t in_addr_t;typedef uint16_t in_port_t;typedef unsign

3、ed short sa_family_t;struct in_addr in_addr_t s_addr;struct sockaddr_in uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero8;struct sockaddr_in serverbzero(&server,sizeof(server)server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_add

4、r.s_addr = htonl(INADDR_ANY).nIPv6地址為128位。(netinet/in.h)套接字地址結(jié)構(gòu)(IPv6)typedef uint16_t in_port_t;typedef unsigned short sa_family_t;struct in6_addr uint8_ts6_addr16;struct sockaddr_in6 uint8_t sin6_len; sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr;si

5、n6_flowinfo成員分成三個(gè)字段:n低24位是流量標(biāo)號(hào);n下4位是優(yōu)先級(jí);n再下4位保留IPv4與IPv6地址結(jié)構(gòu)比較長(zhǎng)度AF_INET616位端口號(hào)32位流標(biāo)簽128位IPv6地址sockaddr_in6 長(zhǎng)度AF_INET16位端口號(hào)32位IP地址未用sockaddr_in 固定長(zhǎng)度(固定長(zhǎng)度(16字節(jié))字節(jié))固定長(zhǎng)度(固定長(zhǎng)度(24字節(jié))字節(jié))n套接口地址結(jié)構(gòu)僅在給定主機(jī)上使用,雖然有些成員用在不同主機(jī)間通信,但結(jié)構(gòu)本身并不參與通信。通用套接字地址結(jié)構(gòu)通用套接字地址結(jié)構(gòu)n由于套接字函數(shù)需接收來(lái)自不同協(xié)議的地址結(jié)構(gòu),ANSI的辦法是使用通用的指針類(lèi)型,即(void *).套接字函數(shù)方

6、法是定義一個(gè)通用的套接字地址結(jié)構(gòu)。struct sockaddr uint8_t sa_len; sa_family_t sa_family; char sa_data14;這就要求調(diào)用套接字函數(shù)時(shí),需將指向特定于協(xié)議的地址結(jié)構(gòu)的指針類(lèi)型轉(zhuǎn)換成指向通用的地址結(jié)構(gòu)的指針, 如: struct sockaddr_in serv bind(sockfd, (struct sockaddr *)&serv,sizeof(serv);字節(jié)排序函數(shù)n為保證”大端“和”小端“字節(jié)序的機(jī)器之間能相互通信,需在發(fā)送多字節(jié)整數(shù)時(shí),將主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序,或反之。高序字節(jié)低序字節(jié)MSB(最高有效位)1

7、6位值 LSB高序字節(jié)低序字節(jié)內(nèi)存地址增大方向內(nèi)存地址增大方向小端字節(jié)序大端字節(jié)序網(wǎng)絡(luò)字節(jié)序字節(jié)排序函數(shù)(cont.)#include uint16_t htons(uint16_t hostshort)uint32_t htonl(uint32_t hostlong) 均返回:網(wǎng)絡(luò)字節(jié)序值uint16_t ntohs(uint16_t netshort)uint32_t ntohl(uint32_t netlong) 均返回:主機(jī)字節(jié)序值h:主機(jī) n:網(wǎng)絡(luò) s:短整數(shù) l:長(zhǎng)整數(shù)字節(jié)操縱函數(shù)n#include void bzero(void *dest, size_t nbytes);voi

8、d bcopy(const void *src, void *dest, size_t nbytes);int bcmp(const void *src, void *dest, size_t nbytes); /*返回0則相同,非0不相同*/ 上述三個(gè)函數(shù)源自BSDvoid *memset(void *dest, int c, size_t len);void *memcpy(void *dest, const void *src, size_t nbytes);int memcmp(const void *ptr1, const void *ptr2, size_t nbytes)上述三個(gè)

9、函數(shù)屬于ANSI C#include #include main() char *s1=Hello, Programmers!; char *s2=Hello, programmers!; int r; r=memcmp(s1,s2,strlen(s1); if(!r) printf(s1 and s2 are identical); else if(r0) printf(s1 less than s2); else printf(s1 greater than s2); Memset函數(shù)#include #include int main() char buffer = This is a

10、test of the memset function; printf( Before: %sn, buffer ); memset( buffer, *, 4 ); printf( After: %sn, buffer ); Before: This is a test of the memset function After: * is a test of the memset function 地址轉(zhuǎn)換函數(shù)#include 將點(diǎn)分十進(jìn)制數(shù)串轉(zhuǎn)換成32位網(wǎng)絡(luò)字節(jié)序二進(jìn)制值。int inet_aton(const char *cp, struct in_addr *inp); 返回:1-串有

11、效,0-串有錯(cuò)in_addr_t inet_addr(const char *cp); 返回:若成功,返回32位二進(jìn)制的網(wǎng)絡(luò)字節(jié)序地址,若有錯(cuò),則返回一個(gè) 常值INADDR_NONE(32位均為1). ninet_aton函數(shù)將cp所指的字符串(點(diǎn)分十進(jìn)制數(shù)串,如192.168.0.1)轉(zhuǎn)換成32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制,并通過(guò)指針inp來(lái)存儲(chǔ)。這個(gè)函數(shù)需要對(duì)字符串所指的地址進(jìn)行有效性驗(yàn)證。但如果cp為空,函數(shù)仍然成功,但不存儲(chǔ)任何結(jié)果。ninet_addr進(jìn)行相同的轉(zhuǎn)換,但不進(jìn)行有效性驗(yàn)證,也就是說(shuō),所有232種可能的二進(jìn)制值對(duì)inet_addr函數(shù)都是有效的。地址轉(zhuǎn)換函數(shù)(續(xù))地址轉(zhuǎn)換函數(shù)(續(xù)

12、)char *inet_ntoa(struct in_addr inaddr); 返回:指向點(diǎn)分十進(jìn)制數(shù)串的指針n函數(shù)inet_ntoa將32位的網(wǎng)絡(luò)字節(jié)序二進(jìn)制IPv4地址轉(zhuǎn)換成相應(yīng)的點(diǎn)分十進(jìn)制數(shù)串。但由于返回值所指向的串留在靜態(tài)內(nèi)存中,這意味著函數(shù)是不可重入的。n需要注意的是這個(gè)函數(shù)是以結(jié)構(gòu)為參數(shù)的,而不是指針。n上述三個(gè)地址轉(zhuǎn)換函數(shù)都只能處理IPv4協(xié)議,而不能處理IPv6地址。地址轉(zhuǎn)換函數(shù)(地址轉(zhuǎn)換函數(shù)(cont.)n#include int inet_pton(int family, const char *strptr, void *addrptr); 返回:1-成功,0輸入無(wú)效,

13、-1:出錯(cuò)const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len); 返回:指向結(jié)果的指針成功,NULL出錯(cuò)字母P和N分別代表presentation(地址的表達(dá)格式)和numeric(數(shù)值格式)。nfamily參數(shù)可以是AF_INET,也可以是AF_INET6。n長(zhǎng)度參數(shù)len是目標(biāo)的大小,如果太小無(wú)法容納表達(dá)格式結(jié)果,則返回一個(gè)空指針。另外,目標(biāo)指針調(diào)用前必須先由調(diào)用者分配空間。inet_pton的實(shí)現(xiàn)(只支持的實(shí)現(xiàn)(只支持IPv4)int inet_pton(int family, c

14、onst char *strptr, void *addrptr) if (family = AF_INET) struct in_addr in_val; if (inet_aton(strptr, &in_val) memcpy(addrptr, &in_val, sizeof(struct in_addr); return (1); return(0); else errno = EAFNOSUPPORT; /*以不被支持的地址族做為family的參數(shù)*/ return (-1); Tcp套接字TCP套接字編程(cont.)socket()bind()listen()ac

15、cept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)socket#include int socket(int family, int type, int protocol) 返回:非負(fù)套接字(sockfd)成功;-1出錯(cuò)。family:協(xié)議族;type:套接字類(lèi)型; protocol:如調(diào)用者不想指定,一般為0 ,表示缺省,原始套接字除外。 family typeAF_INET IPv4協(xié)議SOCK_STREAM 字節(jié)流套接口AF_INET6IPv6協(xié)議

16、SOCK_DGRAM 數(shù)據(jù)報(bào)套接口AF_LOCALunix域協(xié)議SOCK_RAW 原始套接口AF_ROUTE 路由套接口AF_KEY 密鑰套接口Protocol:指明此socket請(qǐng)求所使用的協(xié)議,可以使用如下相關(guān)符號(hào)常數(shù)來(lái)表示。IPPROTO_TCP:表示TCP協(xié)議IPPROTO_UDP:表示UDP協(xié)議 AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY SOCK_STREAM TCP TCP Yes SOCK_DGRAM UDP UDP Yes SOCK_RAW IPv4 IPv6 Yes Yes #include int sockfd; /crteate

17、socketif(sockfd = socket(AF_INET, SOCK_STREAM, 0) = = -1) /handle exceptionTCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)bind#include int bind(int sockfd, const struct sockaddr *addr, socklen_len len) 返回:0成功;-1出

18、錯(cuò)并置errnon該函數(shù)指明套接字將使用本地的哪一個(gè)協(xié)議的參數(shù)進(jìn)行數(shù)據(jù)傳送(IP地址和端口號(hào)),注意:協(xié)議地址addr是通用地址。nLen是該地址結(jié)構(gòu)(第二個(gè)參數(shù))的長(zhǎng)度。n一般而言,服務(wù)器調(diào)用此函數(shù),而客戶(hù)則很少調(diào)用它。 綁定地址時(shí),可以指定地址和端口號(hào),也可以指定其中之一,甚至一個(gè)也不指定。通配地址:INADDR_ANY,其值一般為0,它通知內(nèi)核選擇IP地址。IP地址 端口 結(jié)果通配地址 0 內(nèi)核選擇IP地址和端口號(hào)通配地址 非0 內(nèi)核選擇IP地址,進(jìn)程指定端口本地IP 0 進(jìn)程指定IP地址,內(nèi)核選擇端口本地IP 非0 進(jìn)程指定IP地址和端口號(hào) 若指定端口號(hào)為0,調(diào)用函數(shù)bind時(shí),內(nèi)核選

19、擇一個(gè)臨時(shí)端口(在實(shí)際中,端口號(hào)都要指定);但若指定一個(gè)通配IP地址,則直到套接字已連接(TCP)或數(shù)據(jù)報(bào)已在套接字上發(fā)出(UDP),內(nèi)核才選擇一個(gè)本地IP地址。bind函數(shù)(續(xù))n另外,需要注意以下幾點(diǎn):n參數(shù)addr中的相關(guān)字段在初始化時(shí),必須是網(wǎng)絡(luò)字節(jié)序;n如果由內(nèi)核來(lái)選擇IP地址和臨時(shí)端口號(hào),函數(shù)并不返回所選擇的值。為了獲得這些值,進(jìn)程必須調(diào)用getsockname函數(shù)getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *localaddrlen); n函數(shù)bind返回的一個(gè)常見(jiàn)錯(cuò)誤是:所綁定的地址已被其它進(jìn)程使用

20、,此時(shí)errno的值為EADDRINUSE, 我們可以通過(guò)設(shè)置套接字選項(xiàng)SO_REUSEADDR。setsockopt函數(shù)函數(shù)#include int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 返回:0OK;-1出錯(cuò)。nsockfd必須指向一個(gè)打開(kāi)的套接字描述字。nLevel是選項(xiàng)所在的層及協(xié)議,有如下值: SOL_SOCKET (通用套接字 ) IPPROTO_TCP(傳輸層,TCP協(xié)議) IPPROTO_IP(網(wǎng)際層,IP協(xié)議)nOptname是所要操作的選項(xiàng)名

21、。(SO_、IP_、TCP_是選項(xiàng)名的前綴,不同的level有不同的前綴。)noptval是一個(gè)指向變量的指針,通過(guò)它設(shè)置選項(xiàng)的新值,此變量的大小由最后一個(gè)參數(shù)指定。bind函數(shù)的用法struct sockaddr_in addr;int port = 1234;int opt = 1;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&addr,sizeof(addr);addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_A

22、NY);addr.sin_port = htons(port);if (bind(fd, (struct sockaddr *)&addr, sizeof(addr) = -1)/* 錯(cuò)誤處理 */TCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)listen#include int listen(int sockfd, int backlog) 返回:0成功;-1出錯(cuò)

23、并置errno值;n函數(shù)listen僅被服務(wù)器調(diào)用,它完成兩件事情:n函數(shù)listen將未連接的套接字轉(zhuǎn)化成被動(dòng)套接字,指示內(nèi)核應(yīng)接受指向此套接字的連接請(qǐng)求;n函數(shù)的第二個(gè)參數(shù)規(guī)定了內(nèi)核為此套接字排隊(duì)的最大連接個(gè)數(shù);n對(duì)于給定的監(jiān)聽(tīng)套接字,內(nèi)核要維護(hù)兩個(gè)隊(duì)列n未完成連接隊(duì)列n已完成連接隊(duì)列n兩個(gè)隊(duì)列之和不超過(guò)backlog;客戶(hù)服務(wù)器Connect調(diào)用在未完成隊(duì)列建立條目SYN JSYN K, ack J+1ack K+1該條目從未完成隊(duì)列移至已完成隊(duì)列,acceptConnect返回TCP三路握手和監(jiān)聽(tīng)套接口的兩個(gè)隊(duì)列l(wèi)isten函數(shù)(續(xù))三路握手完成兩隊(duì)列之和不能超過(guò)backlog已完成連

24、接隊(duì)列(ESTABLISHED狀態(tài))未完成連接隊(duì)列(SYN_RCVD狀態(tài))新到達(dá)的SYN分節(jié)服務(wù)器TCPacceptlisten函數(shù)(續(xù))另外幾點(diǎn)說(shuō)明:n不同的實(shí)現(xiàn)對(duì)backlog有不同的解釋?zhuān)缭醋訠erkeley的實(shí)現(xiàn)將backlog增加一個(gè)模糊因子,把它乘以1.5,再作為兩個(gè)隊(duì)列之和;n不要把backlog定義為0,因?yàn)橛行?shí)現(xiàn)允許1個(gè)連接排隊(duì),而有些實(shí)現(xiàn)不允許有連接排隊(duì);n當(dāng)一個(gè)客戶(hù)SYN到達(dá)時(shí),若兩個(gè)隊(duì)列都是滿(mǎn)的,tcp就忽略此分節(jié),且不發(fā)送RST。這是因?yàn)?,這種情況是暫時(shí)的,客戶(hù)tcp將重發(fā)SYN,期望不久的將來(lái)就能在隊(duì)列中找到空閑條目。要是TCP服務(wù)器發(fā)送一個(gè)RST,客戶(hù)的con

25、nect函數(shù)立即返回一個(gè)錯(cuò)誤,強(qiáng)制應(yīng)用進(jìn)程處理這種情況,而不是讓TCP正常的重傳機(jī)制處理。客戶(hù)區(qū)別不了兩種情況,作為SYN的響應(yīng),意為“此端口上沒(méi)有服務(wù)器”的RST和意為“有服務(wù)器在此端口上但其隊(duì)列滿(mǎn)”的RST。TCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)connect#include int connect(int sockfd, const struct sockad

26、dr *addr, socklen_t addrlen); 返回:0成功;-1出錯(cuò);n函數(shù)connect激發(fā)TCP的三路握手過(guò)程;僅在成功或出錯(cuò)返回;錯(cuò)誤有以下幾種情況:n如果客戶(hù)沒(méi)有收到SYN分節(jié)的響應(yīng)(總共75秒,這之間需要可能需要重發(fā)若干次SYN),則返回ETIMEDOUT。n如果對(duì)客戶(hù)的SYN的響應(yīng)是RST,則表明該服務(wù)器主機(jī)在指定的端口上沒(méi)有進(jìn)程在等待與之相連。函數(shù)返回錯(cuò)誤ECONNREFUSED;n如果客戶(hù)發(fā)出的SYN在中間路由器上引發(fā)一個(gè)目的地不可達(dá)ICMP錯(cuò)誤,客戶(hù)上的內(nèi)核保存此消息,并按第一種情況,連續(xù)發(fā)送SYN,直到規(guī)定時(shí)間,返回保存的消息(即ICMP錯(cuò)誤)作為EHOSTU

27、NREACH或ENETUNREACH錯(cuò)誤返回給進(jìn)程。connect函數(shù)(續(xù))n客戶(hù)在調(diào)用connect前不必非得調(diào)用bind函數(shù),此時(shí),內(nèi)核會(huì)選擇一個(gè)合適的IP地址和臨時(shí)端口號(hào);n如果函數(shù)connect失敗,則套接字不可再用,必須關(guān)閉。不能再對(duì)此套接字再調(diào)用函數(shù)connect。int sockfd;struct sockaddr_in server;bzero(&server, sizeof(server);server.sin_family = AF_INET;server.sin_port = htons(1234);server.sin_addr.s_addr= inet_add

28、r(“127.0.0.1”);if (connect(sockfd, (struct sockaddr *)&server, sizeof(server) = -1) /handle exceptionTCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)accept#include int accept(int sockfd, struct sockaddr *cliad

29、dr, socklen_t *addrlen); 返回:非負(fù)描述字(connfd)OK;-1出錯(cuò);naccept函數(shù)由TCP服務(wù)器調(diào)用;從已完成連接隊(duì)列頭返回下一個(gè)已完成連接;如果該隊(duì)列空,則進(jìn)程進(jìn)入睡眠狀態(tài)。n函數(shù)返回的套接字為已連接套接字,應(yīng)與監(jiān)聽(tīng)套接字區(qū)分開(kāi)來(lái)n該函數(shù)最多:一個(gè)既可能是新套接字也可能是錯(cuò)誤指示的整數(shù),一個(gè)客戶(hù)進(jìn)程的協(xié)議地址(由cliaddr所指),以及該地址的大?。ㄟ@后兩個(gè)參數(shù)是值結(jié)果參數(shù));也就是說(shuō),服務(wù)器可以通過(guò)參數(shù)cliaddr來(lái)得到請(qǐng)求連接并獲得成功的客戶(hù)的地址和端口號(hào);accept函數(shù)示例struct sockaddr_inservaddr, cliaddr;s

30、ocklen_tlen;intlistenfd, connfd;connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);if (connfd = -1)/* 出錯(cuò)處理 */printf(“ connection from %s”, inet_ntop(AF_INET, cliaddr.sin_addr, buff, sizeof(buf);TCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write(

31、)read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求TCP服務(wù)器端TCP客戶(hù)端基本套接字函數(shù)close#include int close(int sockfd); 返回:0OK;-1出錯(cuò);nclose函數(shù)缺省功能是將套接字做上“已關(guān)閉”標(biāo)記,并立即返回到進(jìn)程。這個(gè)套接字不能再為該進(jìn)程所用。n正常情況下,close將引發(fā)向TCP的四分節(jié)終止序列,但在終止前將發(fā)送已排隊(duì)的數(shù)據(jù);n如果套接字描述符訪問(wèn)計(jì)數(shù)在調(diào)用close后大于0(在多個(gè)進(jìn)程共享同一個(gè)套接字的情況下),則不會(huì)引發(fā)TCP終止序列(即不會(huì)發(fā)送FIN分節(jié));基本套接字函數(shù)-shutdown#include int shutdown(in

32、t sockfd, int howto); 返回:0OK;-1出錯(cuò),并置相應(yīng)的errno的值;n該函數(shù)立即發(fā)送FIN分節(jié)(無(wú)論其訪問(wèn)計(jì)數(shù)是否大于0)。shutdown根據(jù)參數(shù)howto關(guān)閉指定方向的數(shù)據(jù)傳輸;nSHUT_RD:關(guān)閉連接的讀這一半,不再接收套接字中的數(shù)據(jù)且現(xiàn)留在接收緩沖區(qū)的數(shù)據(jù)作廢;nSHUT_WR :關(guān)閉連接的寫(xiě)這一半(半關(guān)閉),當(dāng)留在套接字發(fā)送緩沖區(qū)中的數(shù)據(jù)都被發(fā)送,后跟tcp連接終止序列,不管訪問(wèn)計(jì)數(shù)是否大于0;此后將不能在執(zhí)行對(duì)套接字的任何寫(xiě)操作;nSHUT_RDWR:連接的讀、寫(xiě)都關(guān)閉,這等效于調(diào)用shutdown兩次,一次調(diào)用是用SHUT_RD,第二次用SHUT_WR

33、。數(shù)據(jù)數(shù)據(jù)FIN數(shù)據(jù)和FIN的確認(rèn)數(shù)據(jù)數(shù)據(jù)FIN數(shù)據(jù)和FIN的確認(rèn)writewriteshutdownRead返回大于0Read返回大于0Read返回0writewritecloseRead返回大于0Read返回0Read返回大于0客戶(hù)服務(wù)器調(diào)用shutdown關(guān)閉一半TCP連接基本套接字函數(shù)read#include int read(int fd, char *buf, int len);返回:大于0讀寫(xiě)字節(jié)大??;-1出錯(cuò);n調(diào)用函數(shù)read時(shí),有如下幾種情況:n套接字接收緩沖區(qū)接收數(shù)據(jù),返回接收到的字節(jié)數(shù);ntcp協(xié)議收到FIN數(shù)據(jù),返回0;ntcp協(xié)議收到RST數(shù)據(jù),返回1,同時(shí)errn

34、o為ECONNRESET;n進(jìn)程阻塞過(guò)程中接收到信號(hào),返回1,同時(shí)errno為EINTR。read(connfd,buff,strlen(buff);基本套接字函數(shù)write#include int write(int fd, char *buf, int len); 返回:大于0讀寫(xiě)字節(jié)大??;-1出錯(cuò);n調(diào)用函數(shù)write,有如下幾種情況:n套接字發(fā)送緩沖區(qū)有足夠空間,返回發(fā)送的字節(jié)數(shù);ntcp協(xié)議接收到RST數(shù)據(jù),返回1,同時(shí)errno為ECONNRESET; ;n進(jìn)程阻塞過(guò)程中接收到信號(hào),返回1,同時(shí)errno為EINTR。write(connfd,buff,strlen(buff);數(shù)

35、據(jù)傳輸函數(shù)send#include #include ssize_t send (int fd, const void *msg, size_t len, int flags); 返回:非0發(fā)送成功的數(shù)據(jù)長(zhǎng)度;-1出錯(cuò);nflags 是傳輸控制標(biāo)志,其值定義如下:n0:常規(guī)操作,如同write()函數(shù)nMSG_OOB,發(fā)送帶外數(shù)據(jù)(TCP緊急數(shù)據(jù))。nMSG_DONTROUTE:忽略底層協(xié)議的路由設(shè)置,只能將數(shù)據(jù)發(fā)送給與發(fā)送機(jī)處在同一個(gè)網(wǎng)絡(luò)中的機(jī)器上。數(shù)據(jù)傳輸函數(shù)recv#include #include ssize_t recv(int fd, void *buf ,size_t len,

36、int flags); 返回:大于0表示成功接收的數(shù)據(jù)長(zhǎng)度;0: 對(duì)方已關(guān)閉,-1:出錯(cuò)。nflags是傳輸控制標(biāo)志,其值定義如下:n0:常規(guī)操作,如同read()函數(shù);nMSG_PEEK:只查看數(shù)據(jù)而不讀出數(shù)據(jù),后續(xù)讀操作仍然能讀出所查看的該數(shù)據(jù);nMSG_OOB:忽略常規(guī)數(shù)據(jù),而只讀帶外數(shù)據(jù);nMSG_WAITALL:recv函數(shù)只有在將接收緩沖區(qū)填滿(mǎn)后才返回。TCP套接字編程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客戶(hù)連接請(qǐng)求T

37、CP服務(wù)器端TCP客戶(hù)端域名解析函數(shù)gethostbyname#include struct hostent *gethostbyname(const char *hostname) 返回:非空指針成功;空指針出錯(cuò),同時(shí)設(shè)置h_errorn該函數(shù)既可解析IPv4地址,也可解析IPv6地址;n該函數(shù)既可接收域名,也可接收點(diǎn)分十進(jìn)制參數(shù)n當(dāng)hostname為點(diǎn)分十進(jìn)制時(shí),函數(shù)并不執(zhí)行網(wǎng)絡(luò)查詢(xún),而是直接將其拷貝到結(jié)果字段中。n此函數(shù)返回的非空指針指向下面的hostent結(jié)構(gòu)結(jié)構(gòu)hostentstruct hostent char *h_name; /* official (canonical) na

38、me of host */char *h_aliases;/* pointer to array of pointers to alias names */int h_addrtype;/* host address type: AF_INET or AF_INET6 */inth_length;/* length of address :4 or 16 */char *h_addr_list;/* ptr to array of ptrs with IPv4 or IPv6 address */;#define h_addr h_addr_list0 /* first address in

39、list */nstruct hostent * he;n*(struct in_addr *)he-h_addr)gethostbyname 返回的信息h_nameh_aliasesh_addrtypeh_lengthh_addr_listhostent 正式主機(jī)名 0別名1 0別名2 0IP地址1IP地址2IP地址3NULLNULLAF_INETin_addr in_addr in_addr if (he = gethostbyname(argv1) = NULL) perror(gethostbyname error.); exit(1); if (fd = socket(AF_INET

40、, SOCK_STREAM, 0) = -1) perror(Create socket failed.); exit(1); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr = *(struct in_addr *) he-h_addr);char *fgets(char *buf, int num, FILE *fp); n從fp指向的文件讀取一個(gè)長(zhǎng)度為num-1的字符串,存入起始地址為buf的空間。返回地址buf,若

41、遇文件結(jié)束或出錯(cuò),返回NULL。n其中,fp使用stdin就是從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù) char sendlineMAXDATASIZE;fgets(sendline, MAXDATASIZE, stdin) #includemain()char s80;fputs(fgets(s,80,stdin),stdout); TCP套接字編程n實(shí)現(xiàn)TCP套接字基本步驟分為服務(wù)器端和客戶(hù)端兩部分:n服務(wù)器端n創(chuàng)建套接字;n綁定套接字;n設(shè)置套接字為監(jiān)聽(tīng)模式,進(jìn)入被動(dòng)接受連接狀態(tài);n接受請(qǐng)求,建立連接n讀寫(xiě)數(shù)據(jù)終止連接TCP套接字編程(cont.)n客戶(hù)端步驟n創(chuàng)建套接字n與遠(yuǎn)程服務(wù)器建立連接n讀/寫(xiě)數(shù)據(jù);終

42、止連接TCP服務(wù)器模板int main(void)int sockfd,connect_sock;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1) perror(“create socket failed.”);exit(-1);/* bind sockfd to some address */* listen */loop if(connect_sock=accept(sockfd,NULL,NULL)=-1) perror(“Accept error.”); exit(-1); /* read and process request */close(c

43、onnect_sock); close(sockfd);TCP客戶(hù)模板/* include some header files */int main(void)int sockfd;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1)perror(“Create socket failed.”);exit(-1);/* connect to server */* send requst and receive response */close(sockfd);TCP套接字例程n采用客戶(hù)/服務(wù)器模式,完成下列功能:n客戶(hù)根據(jù)用戶(hù)提供的IP地址,連接相應(yīng)的服務(wù)器;

44、n服務(wù)器等待客戶(hù)的連接,一旦連接成功,則顯示客戶(hù)的IP地址,并發(fā)歡迎信息給客戶(hù);n客戶(hù)接收服務(wù)器發(fā)送的信息并顯示;TCP服務(wù)器程序/*server.c*/#include #include #include #include #include #define PORT 1234#define BACKLOG 1int main(void) int listenfd, connectfd; struct sockaddr_in server; struct sockaddr_in client; int sin_size; if(listenfd=socket(AF_INET, SOCK_STR

45、EAM, 0)=-1) perror(Create socket failed); exit(-1); TCP服務(wù)器程序(cont.) int opt =1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(li

46、stenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)=-1) perror(Bind error); exit(-1); if (listen(listenfd, BACKLOG) = -1) perror(listen error); exit(-1); TCP服務(wù)器程序(cont.) sin_size = sizeof(struct sockaddr_in); while(1) if (connectfd = accept(listenfd, (struct sockaddr *)&client, &

47、sin_size) = -1) perror(accept error); exit(-1); printf(you get a connection from %sn, inet_ntoa(client.sin_addr); send(connectfd,welcome to my servern,22, 0); close(connectfd); /* while */ close(listenfd);TCP客戶(hù)程序/*client.c*/#include #include #include #include #define PORT 1234#define MAXDATASIZE 100

48、int main(int argc, char *argv) int fd, numbytes; char bufMAXDATASIZE; struct hostent * he; struct sockaddr_in server; if (argc != 2) printf(Usage: %s n, argv0); exit(-1); TCP客戶(hù)程序(cont.)if (he = gethostbyname(argv1) = NULL) perror(gethostbyname error.); exit(1); if (fd = socket(AF_INET, SOCK_STREAM,

49、0) = -1) perror(Create socket failed.); exit(1); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr = *(struct in_addr *) he-h_addr);TCP客戶(hù)程序(cont.)if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr) = -1) perror(connect

50、failed.); exit(1); if( (numbytes = recv(fd, buf, MAXDATASIZE, 0) = -1) perror(recv error.); exit(1); bufnumbytes =0; printf(Server Message: %sn,buf); close(fd);上機(jī)2n客戶(hù)端:n從命令行讀入服務(wù)器的IP地址;并連接到服務(wù)器;n循環(huán)從命令行讀入一行字符串,并傳遞給服務(wù)器,由服務(wù)器對(duì)字符串反轉(zhuǎn),并將結(jié)果返回客戶(hù)程序,如果用戶(hù)輸入的是quit,則關(guān)閉連接;n客戶(hù)程序顯示反轉(zhuǎn)后的字符串;n服務(wù)器端:n循環(huán)接收客戶(hù)的連接請(qǐng)求,并顯示客戶(hù)的IP地址

51、和端口號(hào);n接收并顯示客戶(hù)傳來(lái)的字符串,反轉(zhuǎn)后傳遞給客戶(hù); 三種異常情況n下面我們考慮在以下三種異常情況發(fā)生后,tcp客戶(hù)服務(wù)器程序的反映;n服務(wù)器主機(jī)崩潰n服務(wù)器主機(jī)崩潰后重啟n服務(wù)器主機(jī)關(guān)機(jī)。服務(wù)器主機(jī)崩潰n服務(wù)器主機(jī)崩潰時(shí),已有的網(wǎng)絡(luò)連接上發(fā)不出任何東西。n同時(shí)假設(shè)應(yīng)用程序發(fā)出數(shù)據(jù)后,然后阻塞于從套接字讀取響應(yīng)。n由于服務(wù)器主機(jī)崩潰,因此客戶(hù)tcp會(huì)持續(xù)重傳數(shù)據(jù)分節(jié),試圖從服務(wù)器接收一個(gè)ACK:源自Berkeley的實(shí)現(xiàn)將重傳12次。當(dāng)客戶(hù)tcp最終放棄時(shí),返回給客戶(hù)一個(gè)錯(cuò)誤,此時(shí)錯(cuò)誤是ETIMEDOUT,或者是因?yàn)橹虚g路由器判定服務(wù)器主機(jī)不可達(dá),且以一個(gè)目的地不可達(dá)的ICMP消息響應(yīng),

52、則錯(cuò)誤是EHOSTUNREACH或ENETUNREACH。n通過(guò)設(shè)置套接字選項(xiàng)可以更改tcp持續(xù)重傳等待的超時(shí)時(shí)間。服務(wù)器主機(jī)崩潰后重啟n在這種情況下,如果客戶(hù)在主機(jī)崩潰重啟前不主動(dòng)發(fā)送數(shù)據(jù),那么客戶(hù)是不會(huì)知道服務(wù)器已崩潰的。在服務(wù)器重啟后,客戶(hù)向服務(wù)器發(fā)送一個(gè)數(shù)據(jù)分節(jié);n由于服務(wù)器重啟后丟失了以前的連接信息(盡管在服務(wù)端口上有進(jìn)程監(jiān)聽(tīng),但連接套接字所在的端口無(wú)進(jìn)程等待),因此導(dǎo)致服務(wù)器主機(jī)的tcp響應(yīng)RST;n當(dāng)客戶(hù)tcp收到RST,向客戶(hù)返回錯(cuò)誤,ECONNRESETn如果客戶(hù)對(duì)服務(wù)器的崩潰情況很關(guān)心,即使客戶(hù)不主動(dòng)發(fā)送數(shù)據(jù)也這樣,就需要其他技術(shù)支持(如套接口選項(xiàng)SO_KEEPALIVE或

53、某些客戶(hù)服務(wù)器心跳函數(shù))。服務(wù)器主機(jī)關(guān)機(jī)n當(dāng)Linux主機(jī)關(guān)機(jī)時(shí),由init進(jìn)程給所有運(yùn)行的進(jìn)程發(fā)信號(hào)SIGTERM(我們的服務(wù)器程序可以捕獲該信號(hào),并在信號(hào)處理程序中正常關(guān)閉網(wǎng)絡(luò)連接)。n如果服務(wù)器程序忽略了SIGTERM信號(hào),則init進(jìn)程會(huì)等待一段固定的時(shí)間(通常是5s20s),然后給所有還在運(yùn)行的程序發(fā)信號(hào)SIGKILL(該信號(hào)不能由服務(wù)器程序捕獲);服務(wù)器將由信號(hào)SIGKILL終止,其終止時(shí),所有打開(kāi)的描述字被關(guān)閉,這導(dǎo)致向客戶(hù)發(fā)送FIN分節(jié);n客戶(hù)收到FIN分節(jié)后,能推斷出服務(wù)器將終止服務(wù)。第四章 UDP套接字UDP套接字編程n實(shí)現(xiàn)UDP套接字基本步驟分為服務(wù)器端和客戶(hù)端兩部分:n

54、服務(wù)器端n建立UDP套接字;n綁定套接字到特定地址;n等待并接收客戶(hù)端信息;n處理客戶(hù)端請(qǐng)求;n發(fā)送信息回客戶(hù)端;關(guān)閉套接字;UDP套接字編程(Cont.)n客戶(hù)端步驟n建立UDP套接字;n發(fā)送信息給服務(wù)器;n接收來(lái)自服務(wù)器的信息;關(guān)閉套接字UDP套接字編程(Cont.)socket()bind()sendto()close()socket()sendto()recvfrom()close()阻塞直到接收到客戶(hù)信息UDP服務(wù)器端UDP客戶(hù)端recvfrom()處理客戶(hù)信息UDP數(shù)據(jù)傳輸函數(shù)sendto#include #include ssize_t sendto(int sockfd, co

55、nst void *msg, size_t len, int flags, const struct sockaddr *to, int tolen); 返回:大于0成功發(fā)送數(shù)據(jù)長(zhǎng)度;-1出錯(cuò);nUDP套接字使用無(wú)連接協(xié)議,因此必須使用sendto函數(shù),指明目的地址;nflags是傳輸控制標(biāo)志,其值定義如下:n0:常規(guī)操作,如同write()函數(shù);nMSG_OOB:發(fā)送帶外數(shù)據(jù);nMSG_DONTROUTE:忽略底層路由協(xié)議,直接發(fā)送。UDP數(shù)據(jù)傳輸函數(shù)recvfrom#include #include ssize_t recvfrom(int sockfd, void *buf, size_

56、t len, int flags, struct sockaddr *from, int *fromlen); 返回:大于0成功接收數(shù)據(jù)長(zhǎng)度;-1出錯(cuò);nUDP套接字使用無(wú)連接協(xié)議,因此必須使用recvfrom函數(shù),指明源地址;nflags是傳輸控制標(biāo)志,其值定義如下:n0:常規(guī)操作,如同read()函數(shù);nMSG_PEEK:只察看數(shù)據(jù)而不讀出數(shù)據(jù);nMSG_OOB:忽略常規(guī)數(shù)據(jù),而只讀取帶外數(shù)據(jù);nfrom 和 fromlen 是“值結(jié)果”參數(shù)。內(nèi)核長(zhǎng)度套接口地址結(jié)構(gòu)用戶(hù)進(jìn)程結(jié)果值 當(dāng)函數(shù)被調(diào)用時(shí),結(jié)構(gòu)大小是一值,當(dāng)函數(shù)返回時(shí),結(jié)構(gòu)大小又是一個(gè)結(jié)果,這種參數(shù)類(lèi)型叫值-結(jié)果參數(shù)。參數(shù)是一個(gè)整型

57、指針。bind(int sockfd, const struct sockaddr *addr, socklen_len len)recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen)值值-結(jié)果參數(shù)說(shuō)明結(jié)果參數(shù)說(shuō)明UDP服務(wù)器模板#include #include #inlcude int main(void)int socketfd;if (sockfd = socket(AF_INET, SOCK_DGRAM, 0) = -1) perror(“Create so

58、cket failed.”);exit(1);/* Bind socket to address */ loop /* receive and process data from client */ /* send resuts to client */close(sockfd); UDP客戶(hù)模板#include #include #inlcude int main(void)int sockfd;if (sockfd = socket(AF_INET, SOCK_DGRAM, 0) = -1) perror(“Create socket failed.”);exit(1);/* send d

59、ata to the server */* receive data from the server */close(sockfd);UDP套接字例程n本例程分為服務(wù)器和客戶(hù)兩部分,主要完成如下功能:n客戶(hù)端:n從命令行讀入服務(wù)器的IP地址和要傳送的字符串,并將字符串傳遞給服務(wù)器,由服務(wù)器對(duì)字符串進(jìn)行處理,并將結(jié)果返回客戶(hù)程序;n客戶(hù)程序顯示處理后的字符串;n服務(wù)器端:n接收客戶(hù)的字符串,顯示客戶(hù)IP地址和端口號(hào);n對(duì)接收的客戶(hù)字符串顯示并進(jìn)行處理,并發(fā)送回客戶(hù); UDP服務(wù)器#include #include #include #include #include #define PORT 1234#define MAXDATASIZE 100int mai

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論