日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Socket编程小结

發(fā)布時間:2023/11/29 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Socket编程小结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄:?

什么是?socket?...?1

Internet?套接字的兩種類型...?1

網(wǎng)絡(luò)理論...?2

結(jié)構(gòu)體...?2

本機轉(zhuǎn)換...?3

IP?地址和如何處理它們...?4

socket()函數(shù)...?4

bind()函數(shù)...?4

connect()程序...?5

listen()函數(shù)...?6

accept()函數(shù)...?6

send() and recv()?7

sendto()?和 recvfrom()函數(shù)...?7

close()和shutdown()函數(shù)...?8

getpeername()函數(shù)...?8

gethostname()函數(shù)...?8

域名服務(wù)(DNS)...?8

客戶-服務(wù)器背景知識...?9

簡單的服務(wù)器...?9

簡單的客戶程序...?10

數(shù)據(jù)包 Sockets?11

阻塞...?13

select()--多路同步 I/O..?13



--------------------------------------------------------------------------------?

什么是?socket?

你經(jīng)常聽到人們談?wù)撝?“socket”,或許你還不知道它的確切含義。現(xiàn)在讓我告訴你:它是使用標準Unix?文件描述符?(file descriptor)?和其它程序通訊的方式。什么?你也許聽到一些Unix高手(hacker)這樣說過:“呀,Unix中的一切就是文件!”那個家伙也許正在說到一個事實:Unix?程序在執(zhí)行任何形式的?I/O?的時候,程序是在讀或者寫一個文件描述符。一個文件描述符只是一個和打開的文件相關(guān)聯(lián)的整數(shù)。但是(注意后面的話),這個文件可能是一個網(wǎng)絡(luò)連接,?FIFO,管道,終端,磁盤上的文件或者什么其它的東西。Unix?中所有的東西就是文件!所以,你想和Internet上別的程序通訊的時候,你將要使用到文件描述符。你必須理解剛才的話。現(xiàn)在你腦海中或許冒出這樣的念頭:“那么我從哪里得到網(wǎng)絡(luò)通訊的文件描述符呢?”,這個問題無論如何我都要回答:你利用系統(tǒng)調(diào)用?socket(),它返回套接字描述符?(socket descriptor),然后你再通過它來進行send()和?recv()調(diào)用。 “但是...”,你可能有很大的疑惑,“如果它是個文件描述符,那么為什 么不用一般調(diào)用read()和write()來進行套接字通訊?”簡單的答案是:“你可以使用!”。詳細的答案是:“你可以,但是使用send()和recv()讓你更好的控制數(shù)據(jù)傳輸。”?
存在這樣一個情況:在我們的世界上,有很多種套接字。有DARPA Internet?地址?(Internet?套接字),本地節(jié)點的路徑名?(Unix套接字),CCITT X.25地址?(你可以將X.25?套接字完全忽略)。也許在你的Unix?機器上還有其它的。我們在這里只講第一種:Internet?套接字。

Internet?套接字的兩種類型?
  什么意思?有兩種類型的Internet?套接字?是的。不,我在撒謊。其實還有很多,但是我可不想嚇著你。我們這里只講兩種。除了這些,?我打算另外介紹的?"Raw Sockets"?也是非常強大的,很值得查閱。?
那么這兩種類型是什么呢?一種是"Stream Sockets"(流格式),另外一種是"Datagram Sockets"(數(shù)據(jù)包格式)。我們以后談到它們的時候也會用到?"SOCK_STREAM"?和?"SOCK_DGRAM"。數(shù)據(jù)報套接字有時也叫“無連接套接字”(如果你確實要連接的時候可以用connect()。)?流式套接字是可靠的雙向通訊的數(shù)據(jù)流。如果你向套接字按順序輸出“1,2”,那么它們將按順序“1,2”到達另一邊。它們是無錯誤的傳遞的,有自己的錯誤控制,在此不討論。?
有什么在使用流式套接字?你可能聽說過?telnet,不是嗎?它就使用流式套接字。你需要你所輸入的字符按順序到達,不是嗎?同樣,WWW瀏覽器使用的?HTTP?協(xié)議也使用它們來下載頁面。實際上,當(dāng)你通過端口80 telnet到一個?WWW?站點,然后輸入 “GET pagename” 的時候,你也可以得到?HTML?的內(nèi)容。為什么流式套接字可以達到高質(zhì)量的數(shù)據(jù)傳輸?這是因為它使用了“傳輸控制協(xié)議?(The Transmission Control Protocol)”,也叫 “TCP”?(請參考?RFC-793?獲得詳細資料。)TCP?控制你的數(shù)據(jù)按順序到達并且沒有錯誤。你也許聽到 “TCP” 是因為聽到過 “TCP/IP”。這里的?IP?是指“Internet?協(xié)議”(請參考?RFC-791。) IP?只是處理?Internet?路由而已。

那么數(shù)據(jù)報套接字呢?為什么它叫無連接呢?為什么它是不可靠的呢?有這樣的一些事實:如果你發(fā)送一個數(shù)據(jù)報,它可能會到達,它可能次序顛倒了。如果它到達,那么在這個包的內(nèi)部是無錯誤的。數(shù)據(jù)報也使用?IP?作路由,但是它不使用?TCP。它使用“用戶數(shù)據(jù)報協(xié)議?(User Datagram Protocol)”,也叫 “UDP”?(請參考?RFC-768。)為什么它們是無連接的呢?主要是因為它并不象流式套接字那樣維持一個連接。你只要建立一個包,構(gòu)造一個有目標信息的IP?頭,然后發(fā)出去。無需連接。它們通常使用于傳輸包-包信息。簡單的應(yīng)用程序有:tftp, bootp等等。?
你也許會想:“假如數(shù)據(jù)丟失了這些程序如何正常工作?”我的朋友,每個程序在?UDP?上有自己的協(xié)議。例如,tftp?協(xié)議每發(fā)出的一個被接受到包,收到者必須發(fā)回一個包來說“我收到了!”?(一個“命令正確應(yīng)答”也叫“ACK” 包)。如果在一定時間內(nèi)(例如5秒),發(fā)送方?jīng)]有收到應(yīng)答,它將重新發(fā)送,直到得到?ACK。這一ACK過程在實現(xiàn)?SOCK_DGRAM?應(yīng)用程序的時候非常重要。

網(wǎng)絡(luò)理論?
  既然我剛才提到了協(xié)議層,那么現(xiàn)在是討論網(wǎng)絡(luò)究竟如何工作和一些關(guān)于?SOCK_DGRAM?包是如何建立的例子。當(dāng)然,你也可以跳過這一段, 如果你認為已經(jīng)熟悉的話。現(xiàn)在是學(xué)習(xí)數(shù)據(jù)封裝?(Data Encapsulation)?的時候了!它非常非常重 要。它重要性重要到你在網(wǎng)絡(luò)課程學(xué)(圖1:數(shù)據(jù)封裝)習(xí)中無論如何也得也得掌握它。主要 的內(nèi)容是:一個包,先是被第一個協(xié)議(在這里是TFTP )在它的報頭(也許是報尾)包裝(“封裝”),然后,整個數(shù)據(jù)(包括?TFTP?頭)被另外一個協(xié)議?(在這里是?UDP )封裝,然后下一個( IP ),一直重復(fù)下去,直到硬件(物理)?層(?這里是以太網(wǎng)?)。當(dāng)另外一臺機器接收到包,硬件先剝?nèi)ヒ蕴W(wǎng)頭,內(nèi)核剝?nèi)P和UDP?頭,TFTP程序再剝?nèi)FTP頭,最后得到數(shù)據(jù)。現(xiàn)在我們終于講到聲名狼藉的網(wǎng)絡(luò)分層模型?(Layered Network Model)。這種網(wǎng)絡(luò)模型在描述網(wǎng)絡(luò)系統(tǒng)上相對其它模型有很多優(yōu)點。例如, 你可以寫一個套接字程序而不用關(guān)心數(shù)據(jù)的物理傳輸(串行口,以太網(wǎng),連接單元接口?(AUI)?還是其它介質(zhì)),因為底層的程序會為你處理它們。實際 的網(wǎng)絡(luò)硬件和拓撲對于程序員來說是透明的。不說其它廢話了,我現(xiàn)在列出整個層次模型。如果你要參加網(wǎng)絡(luò)考試,可一定要記住:

應(yīng)用層?(Application)

表示層?(Presentation)

會話層?(Session)

傳輸層(Transport)

網(wǎng)絡(luò)層(Network)

數(shù)據(jù)鏈路層(Data Link)

物理層(Physical)

物理層是硬件(串口,以太網(wǎng)等等)。應(yīng)用層是和硬件層相隔最遠的--它 是用戶和網(wǎng)絡(luò)交互的地方。這個模型如此通用,如果你想,你可以把它作為修車指南。把它對應(yīng) 到?Unix,結(jié)果是:?
應(yīng)用層(Application Layer) (telnet, ftp,等等)傳輸層(Host-to-Host Transport Layer) (TCP, UDP)Internet層(Internet Layer) (IP和路由)網(wǎng)絡(luò)訪問層?(Network Access Layer) (網(wǎng)絡(luò)層,數(shù)據(jù)鏈路層和物理層)

現(xiàn)在,你可能看到這些層次如何協(xié)調(diào)來封裝原始的數(shù)據(jù)了。?
看看建立一個簡單的數(shù)據(jù)包有多少工作?哎呀,你將不得不使用?"cat"?來建立數(shù)據(jù)包頭!這僅僅是個玩笑。對于流式套接字你要作的是?send()?發(fā)送數(shù)據(jù)。對于數(shù)據(jù)報式套接字,你按照你選擇的方式封裝數(shù)據(jù)然后使用sendto()。內(nèi)核將為你建立傳輸層和?Internet?層,硬件完成網(wǎng)絡(luò)訪問層。 這就是現(xiàn)代科技。現(xiàn)在結(jié)束我們的網(wǎng)絡(luò)理論速成班。哦,忘記告訴你關(guān)于路由的事情了。但是我不準備談它,如果你真的關(guān)心,那么參考?IP RFC。

結(jié)構(gòu)體?
  終于談到編程了。在這章,我將談到被套接字用到的各種數(shù)據(jù)類型。因為它們中的一些內(nèi)容很重要了。首先是簡單的一個:socket描述符。它是下面的類型:int?
僅僅是一個常見的?int。從現(xiàn)在起,事情變得不可思議了,而你所需做的就是繼續(xù)看下去。注 意這樣的事實:有兩種字節(jié)排列順序:重要的字節(jié)?(有時叫?"octet",即八位位組)?在前面,或者不重要的字節(jié)在前面。前一種叫“網(wǎng)絡(luò)字節(jié)順序?(Network Byte Order)”。有些機器在內(nèi)部是按照這個順序儲存數(shù)據(jù),而另外 一些則不然。當(dāng)我說某數(shù)據(jù)必須按照?NBO?順序,那么你要調(diào)用函數(shù)(例如?htons() )來將它從本機字節(jié)順序?(Host Byte Order)?轉(zhuǎn)換過來。如果我沒有 提到?NBO,那么就讓它保持本機字節(jié)順序。我的第一個結(jié)構(gòu)(在這個技術(shù)手冊TM中)--struct sockaddr.。這個結(jié)構(gòu) 為許多類型的套接字儲存套接字地址信息:?
struct sockaddr {?
  ?unsigned short sa_family; /*?地址家族, AF_xxx */?
  ?char sa_data[14]; /*14字節(jié)協(xié)議地址*/?
  ?};?
sa_family?能夠是各種各樣的類型,但是在這篇文章中都是?"AF_INET"。?sa_data包含套接字中的目標地址和端口信息。這好像有點 不明智。?
為了處理struct sockaddr,程序員創(chuàng)造了一個并列的結(jié)構(gòu):?struct sockaddr_in ("in"?代表?"Internet"。)?
struct sockaddr_in {?
short int sin_family; /*?通信類型?*/?
unsigned short int sin_port; /*?端口?*/?
struct in_addr sin_addr; /* Internet?地址?*/?
unsigned char sin_zero[8]; /*?與sockaddr結(jié)構(gòu)的長度相同*/?
  ?};?
用這個數(shù)據(jù)結(jié)構(gòu)可以輕松處理套接字地址的基本元素。注意?sin_zero (它被加入到這個結(jié)構(gòu),并且長度和?struct sockaddr?一樣)?應(yīng)該使用函數(shù)?bzero()?或?memset()?來全部置零。 同時,這一重要的字節(jié),一個指向sockaddr_in結(jié)構(gòu)體的指針也可以被指向結(jié)構(gòu)體sockaddr并且代替它。這樣的話即使?socket()?想要的是?struct sockaddr *,你仍然可以使用?struct sockaddr_in,并且在最后轉(zhuǎn)換。同時,注意?sin_family?和?struct sockaddr?中的?sa_family?一致并能夠設(shè)置為?"AF_INET"。最后,sin_port和?sin_addr?必須是網(wǎng)絡(luò)字節(jié)順序?(Network Byte Order)!你也許會反對道:"但是,怎么讓整個數(shù)據(jù)結(jié)構(gòu)?struct in_addr sin_addr?按照網(wǎng)絡(luò)字節(jié)順序呢?"?要知道這個問題的答案,我們就要仔細的看一看這 個數(shù)據(jù)結(jié)構(gòu):?struct in_addr,?有這樣一個聯(lián)合?(unions):?
/* Internet?地址?(一個與歷史有關(guān)的結(jié)構(gòu)) */?
  ?struct in_addr {?
  ?unsigned long s_addr;?
  ?};?
它曾經(jīng)是個最壞的聯(lián)合,但是現(xiàn)在那些日子過去了。如果你聲明?"ina"?是數(shù)據(jù)結(jié)構(gòu)?struct sockaddr_in?的實例,那么?"ina.sin_addr.s_addr"?就儲 存4字節(jié)的?IP?地址(使用網(wǎng)絡(luò)字節(jié)順序)。如果你不幸的系統(tǒng)使用的還是恐怖的聯(lián)合?struct in_addr?,你還是可以放心4字節(jié)的?IP?地址并且和上面 我說的一樣(這是因為使用了“#define”。)

本機轉(zhuǎn)換?
  我們現(xiàn)在到了新的章節(jié)。我們曾經(jīng)講了很多網(wǎng)絡(luò)到本機字節(jié)順序的轉(zhuǎn)換,現(xiàn)在可以實踐了!你能夠轉(zhuǎn)換兩種類型:?short (兩個字節(jié))和?long (四個字節(jié))。這個函數(shù)對于變量類型?unsigned?也適用。假設(shè)你想將?short?從本機字節(jié)順序轉(zhuǎn) 換為網(wǎng)絡(luò)字節(jié)順序。用?"h"?表示?"本機?(host)",接著是?"to",然后用?"n"?表 示?"網(wǎng)絡(luò)?(network)",最后用?"s"?表示?"short":?h-to-n-s,?或者?htons() ("Host to Network Short")。?
太簡單了...?
如果不是太傻的話,你一定想到了由"n","h","s",和?"l"形成的正確 組合,例如這里肯定沒有stolh() ("Short to Long Host")?函數(shù),不僅在這里 沒有,所有場合都沒有。但是這里有:?
htons()--"Host to Network Short"?
  htonl()--"Host to Network Long"?
  ntohs()--"Network to Host Short"?
  ntohl()--"Network to Host Long"?
現(xiàn)在,你可能想你已經(jīng)知道它們了。你也可能想:“如果我想改變?char?的順序要怎么辦呢?” 但是你也許馬上就想到,“用不著考慮的”。你也許會想到:我的?68000?機器已經(jīng)使用了網(wǎng)絡(luò)字節(jié)順序,我沒有必要去調(diào)用?htonl()轉(zhuǎn)換?IP?地址。你可能是對的,但是當(dāng)你移植你的程序到別的機器上的時候,你的程序?qū)⑹ ?梢浦残?#xff01;這里是?Unix?世界!記住:在你將數(shù)據(jù)放到網(wǎng)絡(luò)上的時候,確信它們是網(wǎng)絡(luò)字節(jié)順序的。?
最后一點:為什么在數(shù)據(jù)結(jié)構(gòu)?struct sockaddr_in?中,?sin_addr?和?sin_port?需要轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序,而sin_family?需不需要呢??答案是:?sin_addr?和?sin_port?分別封裝在包的?IP?和?UDP?層。因此,它們必須要 是網(wǎng)絡(luò)字節(jié)順序。但是?sin_family?域只是被內(nèi)核?(kernel)?使用來決定在數(shù) 據(jù)結(jié)構(gòu)中包含什么類型的地址,所以它必須是本機字節(jié)順序。同時,?sin_family?沒有發(fā)送到網(wǎng)絡(luò)上,它們可以是本機字節(jié)順序。

IP?地址和如何處理它們?
現(xiàn)在我們很幸運,因為我們有很多的函數(shù)來方便地操作?IP?地址。沒有必要用手工計算它們,也沒有必要用"<<"操作來儲存成長整字型。首先,假設(shè)你已經(jīng)有了一個sockaddr_in結(jié)構(gòu)體ina,你有一個IP地址"132.241.5.10"要儲存在其中,你就要用到函數(shù)inet_addr(),將IP地址從 點數(shù)格式轉(zhuǎn)換成無符號長整型。使用方法如下:?
ina.sin_addr.s_addr = inet_addr("132.241.5.10");?
注意,inet_addr()返回的地址已經(jīng)是網(wǎng)絡(luò)字節(jié)格式,所以你無需再調(diào)用 函數(shù)htonl()。?
我們現(xiàn)在發(fā)現(xiàn)上面的代碼片斷不是十分完整的,因為它沒有錯誤檢查。 顯而易見,當(dāng)inet_addr()發(fā)生錯誤時返回-1。記住這些二進制數(shù)字?(無符號數(shù))-1僅僅和IP地址255.255.255.255相符合!這可是廣播地址!大錯特 錯!記住要先進行錯誤檢查。?
好了,現(xiàn)在你可以將IP地址轉(zhuǎn)換成長整型了。有沒有其相反的方法呢? 它可以將一個in_addr結(jié)構(gòu)體輸出成點數(shù)格式?這樣的話,你就要用到函數(shù)?inet_ntoa()("ntoa"的含義是"network to ascii"),就像這樣:?
printf("%s",inet_ntoa(ina.sin_addr));?
它將輸出IP地址。需要注意的是inet_ntoa()將結(jié)構(gòu)體in-addr作為一個參數(shù),不是長整形。同樣需要注意的是它返回的是一個指向一個字符的 指針。它是一個由inet_ntoa()控制的靜態(tài)的固定的指針,所以每次調(diào)用inet_ntoa(),它就將覆蓋上次調(diào)用時所得的IP地址。例如:?
char *a1, *a2;?
.?
.?
a1 = inet_ntoa(ina1.sin_addr); /*?這是198.92.129.1 */?
a2 = inet_ntoa(ina2.sin_addr); /*?這是132.241.5.10 */?
printf("address 1: %sn",a1);?
printf("address 2: %sn",a2);?
輸出如下:?
address 1: 132.241.5.10?
address 2: 132.241.5.10?
假如你需要保存這個IP地址,使用strcopy()函數(shù)來指向你自己的字符指針。?
上面就是關(guān)于這個主題的介紹。稍后,你將學(xué)習(xí)將一個類 似"wintehouse.gov"的字符串轉(zhuǎn)換成它所對應(yīng)的IP地址(查閱域名服務(wù),稍后)。

socket()函數(shù)?
我想我不能再不提這個了-下面我將討論一下socket()系統(tǒng)調(diào)用。?
下面是詳細介紹:?
#include <sys/types.h>?
#include <sys/socket.h>?
int socket(int domain, int type, int protocol);?
但是它們的參數(shù)是什么??首先,domain?應(yīng)該設(shè)置成?"AF_INET",就 象上面的數(shù)據(jù)結(jié)構(gòu)struct sockaddr_in?中一樣。然后,參數(shù)?type?告訴內(nèi)核 是?SOCK_STREAM?類型還是?SOCK_DGRAM?類型。最后,把?protocol設(shè)置為?"0"。(注意:有很多種?domain、type,我不可能一一列出了,請看?socket()?的?man幫助。當(dāng)然,還有一個"更好"的方式去得到?protocol。同 時請查閱?getprotobyname()?的?man?幫助。)?
socket()?只是返回你以后在系統(tǒng)調(diào)用種可能用到的?socket?描述符,或 者在錯誤的時候返回-1。全局變量?errno?中將儲存返回的錯誤值。(請參考?perror()?的?man?幫助。)

bind()函數(shù)?
  一旦你有一個套接字,你可能要將套接字和機器上的一定的端口關(guān)聯(lián)起來。(如果你想用listen()來偵聽一定端口的數(shù)據(jù),這是必要一步--MUD?告 訴你說用命令?"telnet x.y.z 6969"。)如果你只想用?connect(),那么這個步 驟沒有必要。但是無論如何,請繼續(xù)讀下去。?
這里是系統(tǒng)調(diào)用?bind()?的大概:?
#include <sys/types.h>?
#include <sys/socket.h>?
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);?
sockfd?是調(diào)用?socket?返回的文件描述符。my_addr?是指向數(shù)據(jù)結(jié)構(gòu)?struct sockaddr?的指針,它保存你的地址(即端口和?IP?地址)?信息。?addrlen?設(shè)置為?sizeof(struct sockaddr)。?
簡單得很不是嗎??再看看例子:?
#include <string.h>?
#include <sys/types.h>?
#include <sys/socket.h>?
#define MYPORT 3490?
main()?
  ?{?
  ?int sockfd;?
  ?struct sockaddr_in my_addr;?
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*需要錯誤檢查?*/?
my_addr.sin_family = AF_INET; /* host byte order */?
  ?my_addr.sin_port = htons(MYPORT); /* short, network byte order */?
  ?my_addr.sin_addr.s_addr = inet_addr("132.241.5.10");?
  ?bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */?
/* don't forget your error checking for bind(): */?
  ?bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));?
  這里也有要注意的幾件事情。my_addr.sin_port?是網(wǎng)絡(luò)字節(jié)順序,?my_addr.sin_addr.s_addr?也是的。另外要注意到的事情是因系統(tǒng)的不同, 包含的頭文件也不盡相同,請查閱本地的?man?幫助文件。?
在?bind()?主題中最后要說的話是,在處理自己的?IP?地址和/或端口的 時候,有些工作是可以自動處理的。?
my_addr.sin_port = 0; /*?隨機選擇一個沒有使用的端口?*/?
  my_addr.sin_addr.s_addr = INADDR_ANY; /*?使用自己的IP地址?*/?
通過將0賦給?my_addr.sin_port,你告訴?bind()?自己選擇合適的端 口。同樣,將?my_addr.sin_addr.s_addr?設(shè)置為?INADDR_ANY,你告訴 它自動填上它所運行的機器的?IP?地址。如果你一向小心謹慎,那么你可能注意到我沒有將?INADDR_ANY?轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序!這是因為我知道內(nèi)部的東西:INADDR_ANY?實際上就 是?0!即使你改變字節(jié)的順序,0依然是0。但是完美主義者說應(yīng)該處處一致,INADDR_ANY或許是12呢?你的代碼就不能工作了,那么就看下面 的代碼:?
my_addr.sin_port = htons(0); /*?隨機選擇一個沒有使用的端口?*/?
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*?使用自己的IP地址?*/?
你或許不相信,上面的代碼將可以隨便移植。我只是想指出,既然你 所遇到的程序不會都運行使用htonl的INADDR_ANY。?
bind()?在錯誤的時候依然是返回-1,并且設(shè)置全局錯誤變量errno。?
在你調(diào)用?bind()?的時候,你要小心的另一件事情是:不要采用小于?1024的端口號。所有小于1024的端口號都被系統(tǒng)保留!你可以選擇從1024?到65535的端口(如果它們沒有被別的程序使用的話)。 你要注意的另外一件小事是:有時候你根本不需要調(diào)用它。如果你使 用?connect()?來和遠程機器進行通訊,你不需要關(guān)心你的本地端口號(就象你在使用?telnet?的時候),你只要簡單的調(diào)用?connect()?就可以了,它會檢查套接字是否綁定端口,如果沒有,它會自己綁定一個沒有使用的本地端 口。

connect()程序?
  現(xiàn)在我們假設(shè)你是個?telnet?程序。你的用戶命令你得到套接字的文件 描述符。你聽從命令調(diào)用了socket()。下一步,你的用戶告訴你通過端口?23(標準?telnet?端口)連接到"132.241.5.10"。你該怎么做呢??幸運的是,你正在閱讀?connect()--如何連接到遠程主機這一章。你可不想讓你的用戶失望。?connect()?系統(tǒng)調(diào)用是這樣的:?
#include <sys/types.h>?
#include <sys/socket.h>?
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);?
sockfd?是系統(tǒng)調(diào)用?socket()?返回的套接字文件描述符。serv_addr?是 保存著目的地端口和?IP?地址的數(shù)據(jù)結(jié)構(gòu)?struct sockaddr。addrlen?設(shè)置 為?sizeof(struct sockaddr)。?
想知道得更多嗎?讓我們來看個例子:?
#include <string.h>?
#include <sys/types.h>?
#include <sys/socket.h>?
#define DEST_IP "132.241.5.10"?
  #define DEST_PORT 23?
main()?
  ?{?
int sockfd;?
struct sockaddr_in dest_addr; /*?目的地址*/?
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*?錯誤檢查?*/?
dest_addr.sin_family = AF_INET; /* host byte order */?
dest_addr.sin_port = htons(DEST_PORT); /* short, network byte order */?
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);?
bzero(&(dest_addr.sin_zero),; /* zero the rest of the struct */?
/* don't forget to error check the connect()! */?
connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));?
  ?.?
  ?.?
  ?.?
  再一次,你應(yīng)該檢查?connect()?的返回值--它在錯誤的時候返回-1,并 設(shè)置全局錯誤變量?errno。同時,你可能看到,我沒有調(diào)用?bind()。因為我不在乎本地的端口號。 我只關(guān)心我要去那。內(nèi)核將為我選擇一個合適的端口號,而我們所連接的 地方也自動地獲得這些信息。一切都不用擔(dān)心。

listen()函數(shù)?
  是換換內(nèi)容得時候了。假如你不希望與遠程的一個地址相連,或者說,僅僅是將它踢開,那你就需要等待接入請求并且用各種方法處理它們。處 理過程分兩步:首先,你聽--listen(),然后,你接受--accept() (請看下面的 內(nèi)容)。?
除了要一點解釋外,系統(tǒng)調(diào)用?listen?也相當(dāng)簡單。?
int listen(int sockfd, int backlog);?
sockfd?是調(diào)用?socket()?返回的套接字文件描述符。backlog?是在進入 隊列中允許的連接數(shù)目。什么意思呢??進入的連接是在隊列中一直等待直到你接受?(accept()?請看下面的文章)連接。它們的數(shù)目限制于隊列的允許。 大多數(shù)系統(tǒng)的允許數(shù)目是20,你也可以設(shè)置為5到10。和別的函數(shù)一樣,在發(fā)生錯誤的時候返回-1,并設(shè)置全局錯誤變量?errno。你可能想象到了,在你調(diào)用?listen()?前你或者要調(diào)用?bind()?或者讓內(nèi)核隨便選擇一個端口。如果你想偵聽進入的連接,那么系統(tǒng)調(diào)用的順序可 能是這樣的:?
socket();?
  bind();?
listen();?
  /* accept()?應(yīng)該在這?*/?
因為它相當(dāng)?shù)拿髁?#xff0c;我將在這里不給出例子了。(在?accept()?那一章的 代碼將更加完全。)真正麻煩的部分在?accept()。

accept()函數(shù)?
  準備好了,系統(tǒng)調(diào)用?accept()?會有點古怪的地方的!你可以想象發(fā)生 這樣的事情:有人從很遠的地方通過一個你在偵聽?(listen())?的端口連接?(connect())?到你的機器。它的連接將加入到等待接受?(accept())?的隊列 中。你調(diào)用?accept()?告訴它你有空閑的連接。它將返回一個新的套接字文件描述符!這樣你就有兩個套接字了,原來的一個還在偵聽你的那個端口, 新的在準備發(fā)送?(send())?和接收?( recv())?數(shù)據(jù)。這就是這個過程!函數(shù)是這樣定義的:?
#include <sys/socket.h>?
int accept(int sockfd, void *addr, int *addrlen);?
sockfd?相當(dāng)簡單,是和?listen()?中一樣的套接字描述符。addr?是個指 向局部的數(shù)據(jù)結(jié)構(gòu)?sockaddr_in?的指針。這是要求接入的信息所要去的地方(你可以測定那個地址在那個端口呼叫你)。在它的地址傳遞給?accept?之 前,addrlen?是個局部的整形變量,設(shè)置為?sizeof(struct sockaddr_in)。?accept?將不會將多余的字節(jié)給?addr。如果你放入的少些,那么它會通過改?
變?addrlen?的值反映出來。?
同樣,在錯誤時返回-1,并設(shè)置全局錯誤變量?errno。?
現(xiàn)在是你應(yīng)該熟悉的代碼片段。?
#include <string.h>?
#include <sys/socket.h>?
#include <sys/types.h>?
#define MYPORT 3490 /*用戶接入端口*/?
#define BACKLOG 10 /*?多少等待連接控制*/?
main()?
  ?{?
  int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */?
  struct sockaddr_in my_addr; /*?地址信息?*/?
  struct sockaddr_in their_addr; /* connector's address information */?
  int sin_size;?
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*?錯誤檢查*/?
my_addr.sin_family = AF_INET; /* host byte order */?
  my_addr.sin_port = htons(MYPORT); /* short, network byte order */?
  my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */?
  bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */?
/* don't forget your error checking for these calls: */?
  bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));?
listen(sockfd, BACKLOG);?
sin_size = sizeof(struct sockaddr_in);?
  new_fd = accept(sockfd, &their_addr, &sin_size);?
  ?.?
  ?.?
  ?.?
注意,在系統(tǒng)調(diào)用?send()?和?recv()?中你應(yīng)該使用新的套接字描述符?new_fd。如果你只想讓一個連接進來,那么你可以使用?close()?去關(guān)閉原來的文件描述符?sockfd?來避免同一個端口更多的連接。

send() and recv()函數(shù)?
  這兩個函數(shù)用于流式套接字或者數(shù)據(jù)報套接字的通訊。如果你喜歡使 用無連接的數(shù)據(jù)報套接字,你應(yīng)該看一看下面關(guān)于sendto()?和?recvfrom()?的章節(jié)。?
send()?是這樣的:?
int send(int sockfd, const void *msg, int len, int flags);?
sockfd?是你想發(fā)送數(shù)據(jù)的套接字描述符(或者是調(diào)用?socket()?或者是?accept()?返回的。)msg?是指向你想發(fā)送的數(shù)據(jù)的指針。len?是數(shù)據(jù)的長度。 把?flags?設(shè)置為?0?就可以了。(詳細的資料請看?send()?的?man page)。?
這里是一些可能的例子:?
char *msg = "Beej was here!";?
  int len, bytes_sent;?
  .?
  .?
  len = strlen(msg);?
  bytes_sent = send(sockfd, msg, len, 0);?
  .?
  .?
  .?
send()?返回實際發(fā)送的數(shù)據(jù)的字節(jié)數(shù)--它可能小于你要求發(fā)送的數(shù) 目! 注意,有時候你告訴它要發(fā)送一堆數(shù)據(jù)可是它不能處理成功。它只是發(fā)送它可能發(fā)送的數(shù)據(jù),然后希望你能夠發(fā)送其它的數(shù)據(jù)。記住,如果?send()?返回的數(shù)據(jù)和?len?不匹配,你就應(yīng)該發(fā)送其它的數(shù)據(jù)。但是這里也有個好消息:如果你要發(fā)送的包很小(小于大約?1K),它可能處理讓數(shù)據(jù)一 次發(fā)送完。最后要說得就是,它在錯誤的時候返回-1,并設(shè)置?errno。?
recv()?函數(shù)很相似:?
int recv(int sockfd, void *buf, int len, unsigned int flags);?
sockfd?是要讀的套接字描述符。buf?是要讀的信息的緩沖。len?是緩 沖的最大長度。flags?可以設(shè)置為0。(請參考recv()?的?man page。) recv()?返回實際讀入緩沖的數(shù)據(jù)的字節(jié)數(shù)。或者在錯誤的時候返回-1, 同時設(shè)置errno。?
很簡單,不是嗎??你現(xiàn)在可以在流式套接字上發(fā)送數(shù)據(jù)和接收數(shù)據(jù)了。 你現(xiàn)在是?Unix?網(wǎng)絡(luò)程序員了!

sendto()?和?recvfrom()函數(shù)?
  “這很不錯啊”,你說,“但是你還沒有講無連接數(shù)據(jù)報套接字呢?” 沒問題,現(xiàn)在我們開始這個內(nèi)容。?
既然數(shù)據(jù)報套接字不是連接到遠程主機的,那么在我們發(fā)送一個包之 前需要什么信息呢??不錯,是目標地址!看看下面的:?
int sendto(int sockfd, const void *msg, int len, unsigned int flags,?
  const struct sockaddr *to, int tolen);?
你已經(jīng)看到了,除了另外的兩個信息外,其余的和函數(shù)?send()?是一樣 的。?to?是個指向數(shù)據(jù)結(jié)構(gòu)?struct sockaddr?的指針,它包含了目的地的?IP?地址和端口信息。tolen?可以簡單地設(shè)置為?sizeof(struct sockaddr)。 和函數(shù)?send()?類似,sendto()?返回實際發(fā)送的字節(jié)數(shù)(它也可能小于 你想要發(fā)送的字節(jié)數(shù)!),或者在錯誤的時候返回?-1。?
相似的還有函數(shù)?recv()?和?recvfrom()。recvfrom()?的定義是這樣的:?
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,? struct sockaddr *from, int *fromlen);?
又一次,除了兩個增加的參數(shù)外,這個函數(shù)和?recv()?也是一樣的。from?是一個指向局部數(shù)據(jù)結(jié)構(gòu)?struct sockaddr?的指針,它的內(nèi)容是源機器的?IP?地址和端口信息。fromlen?是個?int?型的局部指針,它的初始值為sizeof(struct sockaddr)。函數(shù)調(diào)用返回后,fromlen?保存著實際儲存在?from?中的地址的長度。?
recvfrom()?返回收到的字節(jié)長度,或者在發(fā)生錯誤后返回?-1。?
記住,如果你用?connect()?連接一個數(shù)據(jù)報套接字,你可以簡單的調(diào) 用?send()?和?recv()?來滿足你的要求。這個時候依然是數(shù)據(jù)報套接字,依然使用?UDP,系統(tǒng)套接字接口會為你自動加上了目標和源的信息。

close()和shutdown()函數(shù)?
  你已經(jīng)整天都在發(fā)送?(send())?和接收?(recv())?數(shù)據(jù)了,現(xiàn)在你準備關(guān) 閉你的套接字描述符了。這很簡單,你可以使用一般的?Unix?文件描述符 的?close()?函數(shù):?
  close(sockfd);?
它將防止套接字上更多的數(shù)據(jù)的讀寫。任何在另一端讀寫套接字的企 圖都將返回錯誤信息。?
如果你想在如何關(guān)閉套接字上有多一點的控制,你可以使用函數(shù)?shutdown()。它允許你將一定方向上的通訊或者雙向的通訊(就象close()一 樣)關(guān)閉,你可以使用:?
int shutdown(int sockfd, int how);?
sockfd?是你想要關(guān)閉的套接字文件描述復(fù)。how?的值是下面的其中之 一:?
  0?– 不允許接受?
  1?– 不允許發(fā)送?
  2?– 不允許發(fā)送和接受(和?close()?一樣)?
shutdown()?成功時返回?0,失敗時返回?-1(同時設(shè)置?errno。)?如果在無連接的數(shù)據(jù)報套接字中使用shutdown(),那么只不過是讓?send()?和?recv()?不能使用(記住你在數(shù)據(jù)報套接字中使用了?connect?后 是可以使用它們的)。

getpeername()函數(shù)?
  這個函數(shù)太簡單了。?
它太簡單了,以至我都不想單列一章。但是我還是這樣做了。 函數(shù)?getpeername()?告訴你在連接的流式套接字上誰在另外一邊。函數(shù)是這樣的:?
#include <sys/socket.h>?
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);?
sockfd?是連接的流式套接字的描述符。addr?是一個指向結(jié)構(gòu)?struct sockaddr (或者是?struct sockaddr_in)?的指針,它保存著連接的另一邊的信息。addrlen?是一個?int?型的指針,它初始化為?sizeof(struct sockaddr)。函數(shù)在錯誤的時候返回?-1,設(shè)置相應(yīng)的?errno。 一旦你獲得它們的地址,你可以使用?inet_ntoa()?或者?gethostbyaddr()?來打印或者獲得更多的信息。但是你不能得到它的帳號。(如果它運行著愚蠢的守護進程,這是可能的,但是它的討論已經(jīng)超出了本文的范圍,請參考?RFC-1413?以獲得更多的信息。)

gethostname()函數(shù)?
  甚至比?getpeername()?還簡單的函數(shù)是?gethostname()。它返回你程 序所運行的機器的主機名字。然后你可以使用?gethostbyname()?以獲得你 的機器的?IP?地址。?
  下面是定義:?
  #include <unistd.h>?
int gethostname(char *hostname, size_t size);?
參數(shù)很簡單:hostname?是一個字符數(shù)組指針,它將在函數(shù)返回時保存?
主機名。size是hostname?數(shù)組的字節(jié)長度。?
函數(shù)調(diào)用成功時返回?0,失敗時返回?-1,并設(shè)置?errno。

域名服務(wù)(DNS)?
  如果你不知道?DNS?的意思,那么我告訴你,它代表域名服務(wù)(Domain Name Service)。它主要的功能是:你給它一個容易記憶的某站點的地址,它給你?IP?地址(然后你就可以使用?bind(), connect(), sendto()?或者其它 函數(shù))?。當(dāng)一個人輸入:?
  ?$ telnet whitehouse.gov?
telnet?能知道它將連接?(connect())?到?"198.137.240.100"。?
但是這是如何工作的呢??你可以調(diào)用函數(shù)?gethostbyname():?
#include <netdb.h>?
  struct hostent *gethostbyname(const char *name);?
很明白的是,它返回一個指向?struct hostent?的指針。這個數(shù)據(jù)結(jié)構(gòu) 是這樣的:?
  ?struct hostent {?
  ?char *h_name;?
  ?char **h_aliases;?
  ?int h_addrtype;?
  ?int h_length;?
  ?char **h_addr_list;?
  ?};?
  ?#define h_addr h_addr_list[0]?
這里是這個數(shù)據(jù)結(jié)構(gòu)的詳細資料:?
struct hostent:?
  h_name?– 地址的正式名稱。?
  h_aliases?– 空字節(jié)-地址的預(yù)備名稱的指針。?
  h_addrtype?–地址類型;?通常是AF_INET。?
  h_length?– 地址的比特長度。?
  h_addr_list?– 零字節(jié)-主機網(wǎng)絡(luò)地址指針。網(wǎng)絡(luò)字節(jié)順序。?
  h_addr - h_addr_list中的第一地址。?
gethostbyname()?成功時返回一個指向結(jié)構(gòu)體?hostent?的指針,或者 是個空?(NULL)?指針。(但是和以前不同,不設(shè)置errno,h_errno?設(shè)置錯 誤信息。請看下面的?herror()。)?
但是如何使用呢??有時候(我們可以從電腦手冊中發(fā)現(xiàn)),向讀者灌輸 信息是不夠的。這個函數(shù)可不象它看上去那么難用。?
這里是個例子:?
#include <stdio.h>?
  #include <stdlib.h>?
  #include <errno.h>?
  #include <netdb.h>?
  #include <sys/types.h>?
  #include <netinet/in.h>?
int main(int argc, char *argv[])?
  ?{?
  ?struct hostent *h;?
if (argc != 2) { /*?檢查命令行?*/?
  ?fprintf(stderr,"usage: getip addressn");?
  ?exit(1);?
  ?}?
if ((h=gethostbyname(argv[1])) == NULL) { /*?取得地址信息?*/?
  ?herror("gethostbyname");?
  ?exit(1);?
  ?}?
printf("Host name : %sn", h->h_name);?
  printf("IP Address : %sn",inet_ntoa(*((struct in_addr *)h->h_addr)));?
return 0;?
  ?}?
在使用?gethostbyname()?的時候,你不能用?perror()?打印錯誤信息?(因為?errno?沒有使用),你應(yīng)該調(diào)用?herror()。?
相當(dāng)簡單,你只是傳遞一個保存機器名的字符串(例如?"whitehouse.gov")?給?gethostbyname(),然后從返回的數(shù)據(jù)結(jié)構(gòu)?struct hostent?中獲取信息。?
唯一也許讓人不解的是輸出?IP?地址信息。h->h_addr?是一個?char *, 但是?inet_ntoa()?需要的是?struct in_addr。因此,我轉(zhuǎn)換?h->h_addr?成?struct in_addr *,然后得到數(shù)據(jù)。

客戶-服務(wù)器背景知識?
  這里是個客戶--服務(wù)器的世界。在網(wǎng)絡(luò)上的所有東西都是在處理客戶進程和服務(wù)器進程的交談。舉個telnet?的例子。當(dāng)你用?telnet (客戶)通過23?號端口登陸到主機,主機上運行的一個程序(一般叫?telnetd,服務(wù)器)激活。它處理這個連接,顯示登陸界面,等等。

圖2:客戶機和服務(wù)器的關(guān)系?
圖?2?說明了客戶和服務(wù)器之間的信息交換。?
注意,客戶--服務(wù)器之間可以使用SOCK_STREAM、SOCK_DGRAM?或者其它(只要它們采用相同的)。一些很好的客戶--服務(wù)器的例子有?telnet/telnetd、?ftp/ftpd?和?bootp/bootpd。每次你使用?ftp?的時候,在遠 端都有一個?ftpd?為你服務(wù)。?
一般,在服務(wù)端只有一個服務(wù)器,它采用?fork()?來處理多個客戶的連 接。基本的程序是:服務(wù)器等待一個連接,接受?(accept())?連接,然后?fork()?一個子進程處理它。這是下一章我們的例子中會講到的。

簡單的服務(wù)器?
  這個服務(wù)器所做的全部工作是在流式連接上發(fā)送字符串?"Hello, World!n"。你要測試這個程序的話,可以在一臺機器上運行該程序,然后 在另外一機器上登陸:?
  ?$ telnet remotehostname 3490?
remotehostname?是該程序運行的機器的名字。?
服務(wù)器代碼:?
#include <stdio.h>?
  #include <stdlib.h>?
  #include <errno.h>?
  #include <string.h>?
  #include <sys/types.h>?
  #include <netinet/in.h>?
  #include <sys/socket.h>?
  #include <sys/wait.h>?
#define MYPORT 3490 /*定義用戶連接端口*/?
#define BACKLOG 10 /*多少等待連接控制*/?
main()?
  ?{?
  ?int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd?
*/?
  ?struct sockaddr_in my_addr; /* my address information */?
  ?struct sockaddr_in their_addr; /* connector's address information */?
  ?int sin_size;?
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {?
  ?perror("socket");?
  ?exit(1);?
  ?}?

my_addr.sin_family = AF_INET; /* host byte order */?
  ?my_addr.sin_port = htons(MYPORT); /* short, network byte order */?
  ?my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */?
  ?bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */?

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct?
sockaddr))== -1) {?
  ?perror("bind");?
  ?exit(1);?
  ?}?
if (listen(sockfd, BACKLOG) == -1) {?
  ?perror("listen");?
  ?exit(1);?
  ?}?

while(1) { /* main accept() loop */?
  ?sin_size = sizeof(struct sockaddr_in);?
  ?if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,?
  ?&sin_size)) == -1) {?
  ?perror("accept");?
  ?continue;?
  ?}?
  ?printf("server: got connection from %sn",?
  ?inet_ntoa(their_addr.sin_addr));?
  ?if (!fork()) { /* this is the child process */?
  ?if (send(new_fd, "Hello, world!n", 14, 0) == -1)?
  ?perror("send");?
  ?close(new_fd);?
  ?exit(0);?
  ?}?
  ?close(new_fd); /* parent doesn't need this */?
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */?
  ?}?
  ?}?
如果你很挑剔的話,一定不滿意我所有的代碼都在一個很大的main()?函數(shù)中。如果你不喜歡,可以劃分得更細點。?
你也可以用我們下一章中的程序得到服務(wù)器端發(fā)送的字符串。

簡單的客戶程序?
  這個程序比服務(wù)器還簡單。這個程序的所有工作是通過?3490?端口連接到命令行中指定的主機,然后得到服務(wù)器發(fā)送的字符串。?
客戶代碼:?
#include <stdio.h>?
  #include <stdlib.h>?
  #include <errno.h>?
  #include <string.h>?
  #include <sys/types.h>?
  #include <netinet/in.h>?
  #include <sys/socket.h>?
  #include <sys/wait.h>?
#define PORT 3490 /*?客戶機連接遠程主機的端口?*/?
#define MAXDATASIZE 100 /*?每次可以接收的最大字節(jié)?*/?
int main(int argc, char *argv[])?
  ?{?
  ?int sockfd, numbytes;?
  ?char buf[MAXDATASIZE];?
  ?struct hostent *he;?
  ?struct sockaddr_in their_addr; /* connector's address information */?
if (argc != 2) {?
  ?fprintf(stderr,"usage: client hostnamen");?
  ?exit(1);?
  ?}?
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */?
  ?herror("gethostbyname");?
  ?exit(1);?
  ?}?

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {?
  ?perror("socket");?
  ?exit(1);?
  ?}?

their_addr.sin_family = AF_INET; /* host byte order */?
  their_addr.sin_port = htons(PORT); /* short, network byte order */?
  their_addr.sin_addr = *((struct in_addr *)he->h_addr);?
  bzero(&(their_addr.sin_zero),; /* zero the rest of the struct */?
if (connect(sockfd, (struct sockaddr *)&their_addr,sizeof(struct?
sockaddr)) == -1) {?
  ?perror("connect");?
  ?exit(1);?
  ?}?
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {?
  ?perror("recv");?
  ?exit(1);?
  ?}?
buf[numbytes] = '';?
printf("Received: %s",buf);?
close(sockfd);?
return 0;?
  ?}?
注意,如果你在運行服務(wù)器之前運行客戶程序,connect()?將返回?"Connection refused"?信息,這非常有用。

數(shù)據(jù)包?Sockets?
  我不想講更多了,所以我給出代碼?talker.c?和?listener.c。?
listener?在機器上等待在端口?4590?來的數(shù)據(jù)包。talker?發(fā)送數(shù)據(jù)包到 一定的機器,它包含用戶在命令行輸入的內(nèi)容。?
這里就是?listener.c:?
#include <stdio.h>?
  #include <stdlib.h>?
  #include <errno.h>?
  #include <string.h>?
  #include <sys/types.h>?
  #include <netinet/in.h>?
  #include <sys/socket.h>?
  #include <sys/wait.h>?
#define MYPORT 4950 /* the port users will be sending to */?
#define MAXBUFLEN 100?
main()?
  ?{?
  ?int sockfd;?
  ?struct sockaddr_in my_addr; /* my address information */?
  ?struct sockaddr_in their_addr; /* connector's address information */?
  ?int addr_len, numbytes;?
  ?char buf[MAXBUFLEN];?
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {?
  ?perror("socket");?
  ?exit(1);?
  ?}?
my_addr.sin_family = AF_INET; /* host byte order */?
  ?my_addr.sin_port = htons(MYPORT); /* short, network byte order */?
  ?my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */?
  ?bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */?
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))?

  ?== -1) {?
  ?perror("bind");?
  ?exit(1);?
  ?}?
addr_len = sizeof(struct sockaddr);?
  ?if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0,?
  ?(struct sockaddr *)&their_addr, &addr_len)) == -1) {?
  ?perror("recvfrom");?
  ?exit(1);?
  ?}?
printf("got packet from %sn",inet_ntoa(their_addr.sin_addr));?
  ?printf("packet is %d bytes longn",numbytes);?
  ?buf[numbytes] = '';?
  ?printf("packet contains "%s"n",buf);?
close(sockfd);?
  ?}?
注意在我們的調(diào)用?socket(),我們最后使用了?SOCK_DGRAM。同時, 沒有必要去使用?listen()?或者?accept()。我們在使用無連接的數(shù)據(jù)報套接 字!?
下面是?talker.c:?
#include <stdio.h>?
  #include <stdlib.h>?
  #include <errno.h>?
  #include <string.h>?
  #include <sys/types.h>?
  #include <netinet/in.h>?
  #include <sys/socket.h>?
  #include <sys/wait.h>?
#define MYPORT 4950 /* the port users will be sending to */?
int main(int argc, char *argv[])?
  ?{?
  ?int sockfd;?
  ?struct sockaddr_in their_addr; /* connector's address information */?
  ?struct hostent *he;?
  ?int numbytes;?

if (argc != 3) {?
  ?fprintf(stderr,"usage: talker hostname messagen");?
  ?exit(1);?
  ?}?

if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */?
  ?herror("gethostbyname");?
  ?exit(1);?
  ?}?

if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {?
  ?perror("socket");?
  ?exit(1);?
  ?}?

their_addr.sin_family = AF_INET; /* host byte order */?
  ?their_addr.sin_port = htons(MYPORT); /* short, network byte order?
*/?
  ?their_addr.sin_addr = *((struct in_addr *)he->h_addr);?
  ?bzero(&(their_addr.sin_zero),; /* zero the rest of the struct */?
if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,?
  ?(struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) {?
  ?perror("sendto");?
  ?exit(1);?
  ?}?
printf("sent %d bytes to?
%sn",numbytes,inet_ntoa(their_addr.sin_addr));?
close(sockfd);?
return 0;?
  ?}?
這就是所有的了。在一臺機器上運行?listener,然后在另外一臺機器上 運行?talker。觀察它們的通訊!?
除了一些我在上面提到的數(shù)據(jù)套接字連接的小細節(jié)外,對于數(shù)據(jù)套接 字,我還得說一些,當(dāng)一個講話者呼叫connect()函數(shù)時并指定接受者的地址時,從這點可以看出,講話者只能向connect()函數(shù)指定的地址發(fā)送和接受信息。因此,你不需要使用sendto()和recvfrom(),你完全可以用send()?和recv()代替。

阻塞?
  阻塞,你也許早就聽說了。"阻塞"是?"sleep"?的科技行話。你可能注意到前面運行的?listener?程序,它在那里不停地運行,等待數(shù)據(jù)包的到來。實際在運行的是它調(diào)用?recvfrom(),然后沒有數(shù)據(jù),因此?recvfrom()說"?阻塞?(block)",直到數(shù)據(jù)的到來。?
很多函數(shù)都利用阻塞。accept()?阻塞,所有的?recv*()?函數(shù)阻塞。它 們之所以能這樣做是因為它們被允許這樣做。當(dāng)你第一次調(diào)用?socket()?建立套接字描述符的時候,內(nèi)核就將它設(shè)置為阻塞。如果你不想套接字阻塞,你就要調(diào)用函數(shù)?fcntl():?
#include <unistd.h>?
  #include <fontl.h>?
  ?.?
  ?.?
  ?sockfd = socket(AF_INET, SOCK_STREAM, 0);?
  ?fcntl(sockfd, F_SETFL, O_NONBLOCK);?
  ?.?
  ?.?
  通過設(shè)置套接字為非阻塞,你能夠有效地"詢問"套接字以獲得信息。如果你嘗試著從一個非阻塞的套接字讀信息并且沒有任何數(shù)據(jù),它不允許阻 塞--它將返回?-1?并將?errno?設(shè)置為?EWOULDBLOCK。?
但是一般說來,這種詢問不是個好主意。如果你讓你的程序在忙等狀 態(tài)查詢套接字的數(shù)據(jù),你將浪費大量的?CPU?時間。更好的解決之道是用下一章講的?select()?去查詢是否有數(shù)據(jù)要讀進來。

select()--多路同步?I/O?
  雖然這個函數(shù)有點奇怪,但是它很有用。假設(shè)這樣的情況:你是個服務(wù)器,你一邊在不停地從連接上讀數(shù)據(jù),一邊在偵聽連接上的信息。 沒問題,你可能會說,不就是一個?accept()?和兩個?recv()?嗎??這么 容易嗎,朋友??如果你在調(diào)用?accept()?的時候阻塞呢??你怎么能夠同時接受?recv()?數(shù)據(jù)??“用非阻塞的套接字啊!” 不行!你不想耗盡所有的?CPU?吧??那么,該如何是好??
select()?讓你可以同時監(jiān)視多個套接字。如果你想知道的話,那么它就 會告訴你哪個套接字準備讀,哪個又準備寫,哪個套接字又發(fā)生了例外?(exception)。?
閑話少說,下面是?select():?
#include <sys/time.h>?
  #include <sys/types.h>?
  #include <unistd.h>?
int select(int numfds, fd_set *readfds, fd_set *writefds,fd_set?
*exceptfds, struct timeval *timeout);?
這個函數(shù)監(jiān)視一系列文件描述符,特別是?readfds、writefds?和?exceptfds。如果你想知道你是否能夠從標準輸入和套接字描述符?sockfd?讀入數(shù)據(jù),你只要將文件描述符?0?和?sockfd?加入到集合?readfds?中。參 數(shù)numfds?應(yīng)該等于最高的文件描述符的值加1。在這個例子中,你應(yīng)該 設(shè)置該值為?sockfd+1。因為它一定大于標準輸入的文件描述符?(0)。 當(dāng)函數(shù)?select()?返回的時候,readfds?的值修改為反映你選擇的哪個文件描述符可以讀。你可以用下面講到的宏?FD_ISSET()?來測試。 在我們繼續(xù)下去之前,讓我來講講如何對這些集合進行操作。每個集合類型都是?fd_set。下面有一些宏來對這個類型進行操作:?
FD_ZERO(fd_set *set)?– 清除一個文件描述符集合?
  FD_SET(int fd, fd_set *set) -?添加fd到集合?
  FD_CLR(int fd, fd_set *set)?– 從集合中移去fd?
  FD_ISSET(int fd, fd_set *set)?– 測試fd是否在集合中?
最后,是有點古怪的數(shù)據(jù)結(jié)構(gòu)?struct timeval。有時你可不想永遠等待別人發(fā)送數(shù)據(jù)過來。也許什么事情都沒有發(fā)生的時候你也想每隔96秒在終 端上打印字符串?"Still Going..."。這個數(shù)據(jù)結(jié)構(gòu)允許你設(shè)定一個時間,如果時間到了,而?select()?還沒有找到一個準備好的文件描述符,它將返回讓你繼續(xù)處理。?
數(shù)據(jù)結(jié)構(gòu)?struct timeval?是這樣的:?
struct timeval {?
  ?int tv_sec; /* seconds */?
  ?int tv_usec; /* microseconds */?
  ?};?
只要將?tv_sec?設(shè)置為你要等待的秒數(shù),將?tv_usec?設(shè)置為你要等待的微秒數(shù)就可以了。是的,是微秒而不是毫秒。1,000微秒等于1毫秒,1,000?毫秒等于1秒。也就是說,1秒等于1,000,000微秒。為什么用符號?"usec"呢??字母?"u"?很象希臘字母?Mu,而?Mu?表示?"微"?的意思。當(dāng)然,函數(shù) 返回的時候?timeout?可能是剩余的時間,之所以是可能,是因為它依賴于 你的?Unix?操作系統(tǒng)。?
哈!我們現(xiàn)在有一個微秒級的定時器!別計算了,標準的?Unix?系統(tǒng) 的時間片是100毫秒,所以無論你如何設(shè)置你的數(shù)據(jù)結(jié)構(gòu)?struct timeval,你都要等待那么長的時間。?
還有一些有趣的事情:如果你設(shè)置數(shù)據(jù)結(jié)構(gòu)?struct timeval?中的數(shù)據(jù)為?0,select()?將立即超時,這樣就可以有效地輪詢集合中的所有的文件描述符。如果你將參數(shù)?timeout?賦值為?NULL,那么將永遠不會發(fā)生超時,即一直等到第一個文件描述符就緒。最后,如果你不是很關(guān)心等待多長時間,那么就把它賦為?NULL?吧。?
下面的代碼演示了在標準輸入上等待?2.5?秒:?
#include <sys/time.h>?
  #include <sys/types.h>?
  #include <unistd.h>?
#define STDIN 0 /* file descriptor for standard input */?
main()?
  ?{?
  struct timeval tv;?
  fd_set readfds;?
tv.tv_sec = 2;?
  tv.tv_usec = 500000;?
FD_ZERO(&readfds);?
  FD_SET(STDIN, &readfds);?
/* don't care about writefds and exceptfds: */?
  select(STDIN+1, &readfds, NULL, NULL, &tv);?
if (FD_ISSET(STDIN, &readfds))?
  printf("A key was pressed!n");?
  else?
  printf("Timed out.n");?
  }?
如果你是在一個?line buffered?終端上,那么你敲的鍵應(yīng)該是回車?(RETURN),否則無論如何它都會超時。?
現(xiàn)在,你可能回認為這就是在數(shù)據(jù)報套接字上等待數(shù)據(jù)的方式--你是對 的:它可能是。有些?Unix?系統(tǒng)可以按這種方式,而另外一些則不能。你 在嘗試以前可能要先看看本系統(tǒng)的?man page?了。?
最后一件關(guān)于?select()?的事情:如果你有一個正在偵聽?(listen())?的套 接字,你可以通過將該套接字的文件描述符加入到?readfds?集合中來看是否有新的連接。?
這就是我關(guān)于函數(shù)select()?要講的所有的東西。

專注于企業(yè)信息化,最近對股票數(shù)據(jù)分析較為感興趣,可免費分享股票個股主力資金實時變化趨勢分析工具,股票交流QQ群:457394862

分類:?C/C++


本文轉(zhuǎn)自滄海-重慶博客園博客,原文鏈接:http://www.cnblogs.com/omygod/archive/2007/05/11/742559.html,如需轉(zhuǎn)載請自行聯(lián)系原作者

總結(jié)

以上是生活随笔為你收集整理的Socket编程小结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

adc在线观看| 国产激情免费 | 精品一区二区三区四区在线 | 99精品国产在热久久 | 欧美日韩高清在线一区 | 久久久亚洲精华液 | 在线久久| 久久伊人国产精品 | 嫩草91影院| 久久这里只有精品视频99 | 欧美在线观看小视频 | 永久免费毛片在线观看 | 国内精品久久久久影院优 | 91一区二区三区久久久久国产乱 | 亚洲二区精品 | 久久高清免费视频 | 免费高清在线观看成人 | 又紧又大又爽精品一区二区 | 久久久久99999 | 日本电影黄色 | www日韩在线观看 | 久久草| 丝袜美女在线观看 | 欧美另类人妖 | 成人app在线播放 | 91av99 | 狠狠狠狠狠狠狠狠 | 亚洲国产精品免费 | 久久这里只精品 | 最新精品国产 | 美女黄网站视频免费 | 成人电影毛片 | 亚洲日本三级 | 在线观看的a站 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 国产一区二区精品 | 免费看国产a | 欧美日韩免费在线视频 | 久久99精品久久久久蜜臀 | 国产日韩欧美在线 | www国产在线 | 超碰在线人人爱 | 一本一道久久a久久精品 | 综合网av | 国产精品欧美久久久久无广告 | 日韩精品观看 | 欧美va电影| 亚洲精品久久久久中文字幕m男 | 亚洲免费高清视频 | 视频在线91 | 又黄又爽又刺激的视频 | 日韩欧美在线不卡 | 亚洲精品在线一区二区三区 | 日韩综合精品 | 天天操天天操天天操天天操天天操 | 免费又黄又爽 | 综合五月| 婷婷免费在线视频 | 久久理论电影网 | 欧美激情精品久久久久 | 粉嫩aⅴ一区二区三区 | 亚洲黄色app | 国产精品久久 | av一区二区三区在线观看 | 国产精品亚 | 亚洲一区日韩精品 | 国产一区二区成人 | 国产在线视频在线观看 | 99精品视频在线观看视频 | 草久视频在线观看 | 国产一区二区三区高清播放 | 色婷婷亚洲综合 | 在线观看免费版高清版 | 99欧美| 91中文在线视频 | 黄色av一区二区三区 | 黄污在线看| 在线免费黄色 | 综合网伊人| 亚洲一区二区三区毛片 | av福利在线免费观看 | 超碰在线人人 | 特级西西www44高清大胆图片 | 日本高清xxxx | 色综合婷婷 | 91看成人 | 国产精品美女久久久久久免费 | 午夜婷婷在线观看 | 亚洲精品乱码久久 | av官网在线| 精品色综合 | 国产高清99 | 激情五月婷婷 | 婷婷六月天在线 | 久久五月婷婷综合 | 亚洲成人精品在线观看 | 91在线蜜桃臀 | 欧美国产在线看 | 国产一区在线精品 | 成年人精品 | 97视频在线观看网址 | 久草视频在线免费播放 | 日韩av线观看 | 99中文字幕视频 | 黄色福利网站 | 成人av亚洲| 国产成人精品av在线观 | 日韩久久精品一区二区 | 欧美亚洲精品一区 | 高清免费在线视频 | 探花视频免费在线观看 | 亚洲综合色av | 日韩av在线一区二区 | 丁香六月在线观看 | 亚洲最大av网 | 91成人小视频 | 精品黄色在线观看 | 久久不射电影网 | 日韩精品久久久久久久电影竹菊 | 成人免费观看视频网站 | 欧美日韩精品在线免费观看 | 日批网站免费观看 | 国产视频亚洲视频 | 色亚洲激情 | 国产免费又爽又刺激在线观看 | 久久尤物电影视频在线观看 | 中文字幕在线观看第一区 | 亚洲欧洲精品视频 | 一级黄色视屏 | 成人性生交大片免费观看网站 | 在线有码中文字幕 | 亚洲午夜精品久久久久久久久 | 日韩美视频 | 久久久午夜视频 | 亚洲天天综合 | av日韩在线网站 | 久草视频免费播放 | 91在线视频观看免费 | 久久国语 | 肉色欧美久久久久久久免费看 | 黄色特级毛片 | 粉嫩aⅴ一区二区三区 | 在线国产高清 | 日韩精品免费一线在线观看 | 丁香婷婷成人 | 手机成人在线电影 | 五月婷婷视频在线 | 狠狠狠色丁香综合久久天下网 | 成人影片在线播放 | 国产精品电影在线 | a级国产乱理论片在线观看 特级毛片在线观看 | 久久人人精 | 中文在线免费一区三区 | 久久精品屋 | 久操视频在线观看 | 在线黄色av电影 | 在线看一级片 | 国产一级黄色免费看 | 色综合久久久久综合体桃花网 | 精品国产成人在线 | 91在线麻豆 | 午夜av色 | 91最新网址在线观看 | 精品字幕| 久久国产一区二区三区 | 国产丝袜网站 | 91天天操 | 中文字幕a∨在线乱码免费看 | 亚洲精品456在线播放 | 精品久久久久免费极品大片 | 久久久久久97三级 | 91探花系列在线播放 | 国产精品久久久久影院 | 天天操夜夜操夜夜操 | 午夜狠狠干 | 欧美日本在线视频 | 黄av资源| 午夜影院三级 | 丁香电影小说免费视频观看 | 久久久激情网 | 色五丁香| 国产伦精品一区二区三区无广告 | 日韩欧美有码在线 | 在线视频a| 97操操操 | 国产女人免费看a级丨片 | 精品国产一区二区三区四区vr | 区一区二在线 | 欧美精彩视频在线观看 | 亚洲婷婷在线视频 | 日本中文字幕在线免费观看 | 欧美国产高清 | 精品国产乱码久久久久久1区二区 | 中文字幕亚洲欧美 | 免费观看mv大片高清 | 日日碰夜夜爽 | 久久精品视频网 | 国产一级性生活 | 九九在线国产视频 | 国产成人高清在线 | 国产精品大片在线观看 | 国语自产偷拍精品视频偷 | 天天射天天操天天色 | 久久艹国产 | 丁香婷婷综合激情 | 免费三级在线 | 国产专区在线看 | 国产精品美女免费 | 久久国产成人午夜av影院宅 | 国产v在线| 免费观看xxxx9999片 | 久久国产精品电影 | 夜夜夜| 51久久夜色精品国产麻豆 | 国产成人61精品免费看片 | 久久国产精品二国产精品中国洋人 | 国产美女免费观看 | 国产一区二区三区久久久 | 久久久久女人精品毛片九一 | 国产精品一区二区三区免费看 | 国产另类av | 色综合久久久久 | 天天骚夜夜操 | 久久在线视频精品 | 久久草在线精品 | 免费高清在线视频一区· | 欧美日韩亚洲在线观看 | 国产麻豆电影在线观看 | 久艹视频在线免费观看 | 久久乐九色婷婷综合色狠狠182 | 久久国产精品99久久久久久进口 | 黄色成人av| www.狠狠插.com| 欧美日本啪啪无遮挡网站 | 欧美经典久久 | 欧美日韩国内在线 | av中文字幕av | 精品国产一区二区三区蜜臀 | 久久国产精品一区二区 | 天天看天天干 | 中文有码在线视频 | 日本三级大片 | 美女福利视频在线 | 成人夜晚看av | 17videosex性欧美 | 久久久国产成人 | 在线看欧美| 精品国产美女 | 日韩精品专区 | 99久久久国产免费 | 99精品国产一区二区三区麻豆 | 麻豆94tv免费版 | 91人人爱 | 黄色小说视频在线 | 99久久婷婷国产精品综合 | 一区二区三区在线免费 | 奇米影视777四色米奇影院 | 国产大陆亚洲精品国产 | 国产成人精品在线播放 | 欧美日韩精品在线观看 | 91麻豆精品一区二区三区 | 国产91粉嫩白浆在线观看 | 在线观看日韩中文字幕 | 婷婷激情综合网 | 99re久久精品国产 | 亚洲精品小视频在线观看 | 欧美不卡视频在线 | 黄色一区三区 | 国产在线综合视频 | 99久久婷婷 | 四虎国产精品免费观看视频优播 | 欧美精品久久久久性色 | 欧美极品少妇xxxx | 久久免费视频在线观看6 | 成人羞羞视频在线观看免费 | 天天干,天天操,天天射 | 亚洲免费av观看 | 久久久久人人 | 午夜精品一二区 | 精品亚洲免费 | 国产尤物一区二区三区 | 亚洲天堂免费视频 | 91高清视频在线 | 五月婷视频 | 中文字幕在线高清 | 日韩毛片久久久 | 国产亚洲精品美女久久 | 久久97超碰 | 久香蕉 | 日韩高清国产精品 | 欧美精品做受xxx性少妇 | 久久国产精品视频 | 日日爱网站 | 亚洲精品视频在线免费 | 国产精品一区二区果冻传媒 | 久久久久久久久久久久亚洲 | 麻豆国产网站入口 | 国产精品自在线拍国产 | 99久久日韩精品免费热麻豆美女 | 免费日韩视 | 国产美女精品在线 | 国产大陆亚洲精品国产 | 六月丁香在线视频 | 欧美精品久久久久久久久老牛影院 | 中文字幕免费国产精品 | 成人黄大片 | 精品一区二区免费视频 | 欧美一级淫片videoshd | 日韩久久精品一区二区三区下载 | 国产婷婷色 | 99精品在线免费观看 | 97成人啪啪网 | 超级碰碰免费视频 | 亚洲爱爱视频 | 一区二区三区在线观看免费视频 | 香蕉视频网站在线观看 | 国精产品999国精产品视频 | 久久精品4| 精品一区精品二区 | 黄色一级免费 | 国产高清99 | 亚洲精品色视频 | 久久免费美女视频 | 国产在线欧美在线 | 久久久国产一区二区三区四区小说 | 91成人免费看 | 在线 国产 亚洲 欧美 | 天天操天天干天天干 | 久草五月| 激情婷婷 | 成人91视频| 久久夜夜操 | 国产群p视频 | 色狠狠久久av五月综合 | 99色免费视频 | 久久久久久久久久久免费 | 日日夜夜天天 | 九九在线精品视频 | 超碰人人乐 | 在线欧美国产 | 黄色在线观看网站 | 麻豆影视在线观看 | 亚洲区视频在线观看 | 天天爽天天碰狠狠添 | 亚洲资源网 | 日韩网站免费观看 | 久久国产精品一二三区 | 午夜久久美女 | 一级精品视频在线观看宜春院 | a级片网站 | 日韩欧美精品在线观看 | 欧美 高跟鞋交 xxxxhd | 国产 一区二区三区 在线 | 精品久久亚洲 | 日韩理论在线播放 | 粉嫩av一区二区三区四区五区 | 青草视频免费观看 | 欧美日韩观看 | 欧美日韩午夜爽爽 | 欧美日韩在线视频一区二区 | 国产精品久久久久久久久搜平片 | 黄色影院在线播放 | 99精品视频免费观看视频 | 国产不卡毛片 | 国产一区二区不卡在线 | 免费在线播放视频 | 亚洲天天在线日亚洲洲精 | 国产福利精品视频 | 色福利网| 日韩中文在线观看 | 91亚洲精品国偷拍 | 精品在线视频观看 | 久久精品中文字幕少妇 | 最近2019好看的中文字幕免费 | 亚洲禁18久人片 | 97在线视频网站 | 日日夜夜操操 | 免费在线观看国产黄 | 男女啪啪网站 | 久草在线费播放视频 | 99久久久久久国产精品 | 中文字幕乱在线伦视频中文字幕乱码在线 | 超碰人在线 | 日韩在线视频免费播放 | 欧洲一区精品 | 午夜久久精品 | 久久97久久 | 免费观看第二部31集 | 久久婷婷开心 | 黄色国产在线观看 | 91精品免费视频 | 最近2019好看的中文字幕免费 | 狠狠躁日日躁 | 国产欧美在线一区二区三区 | 国产精品久久久久久久免费大片 | 久久激情网站 | 国产露脸91国语对白 | 日本激情视频中文字幕 | 丁香久久久 | 免费福利视频网 | 激情在线网站 | 精品国产自在精品国产精野外直播 | 中文在线资源 | 视频成人| 久久成人午夜 | 亚洲综合视频在线 | 99电影456麻豆 | 在线国产日本 | 国产精品毛片久久蜜 | 欧美精品你懂的 | 激情视频一区 | 国产中文字幕网 | 特级西西人体444是什么意思 | 玖玖爱国产在线 | 久 久久影院 | 综合久久久久久久 | 伊人色综合久久天天网 | 免费97视频| 五月天丁香综合 | 亚洲午夜精品在线观看 | 久久久久久国产精品久久 | 日韩精品一区电影 | 国内精品视频一区二区三区八戒 | 又色又爽又激情的59视频 | 中文字幕在线国产精品 | 免费观看的黄色片 | 日本精品小视频 | 日韩精品一区二区三区高清免费 | 久久毛片网站 | 国产一区二区精品久久 | 不卡视频一区二区三区 | 日韩电影在线观看一区 | 国产精品 亚洲精品 | 欧美99精品 | 最近日本中文字幕 | 日本久久91 | 精品久久久久亚洲 | 国产精品一区二区三区久久 | 亚洲aⅴ一区二区三区 | 五月天亚洲激情 | 色av男人的天堂免费在线 | 久久激情视频免费观看 | 97视频在线 | 最新av电影网站 | 99视频国产在线 | 精品久久久久久国产91 | 久久午夜精品视频 | 97精品国自产拍在线观看 | 成人av一二三区 | 在线观看免费国产小视频 | 91视频a | 日韩网| 久久黄色网 | 91成人在线视频观看 | 亚洲欧美激情精品一区二区 | 91麻豆传媒 | 在线观看日韩精品 | 精品国产_亚洲人成在线 | 日韩精品一区二区三区高清免费 | 在线视频精品播放 | 亚洲一区二区三区四区在线视频 | 东方av在线免费观看 | 日韩欧美国产成人 | 毛片二区 | 五月在线视频 | a在线播放 | 亚洲黄色成人网 | 亚洲国产中文在线 | 亚洲精品乱码白浆高清久久久久久 | 国产精品青草综合久久久久99 | 精品a在线 | 五月天激情综合网 | 日日干日日 | 久久艹国产视频 | 成年人在线 | 亚洲理论视频 | 国产精品一区二区中文字幕 | 欧美精品一区二区免费 | 极品美女被弄高潮视频网站 | 久久理论视频 | 国产高清久久 | 中国一级片在线观看 | 97国产超碰 | 免费在线观看av的网站 | 日韩午夜一级片 | 亚洲精品大片www | 日本超碰在线 | 色999视频| 国产精品免费一区二区三区 | 中文字幕亚洲在线观看 | 狠狠色婷婷丁香六月 | 久久久精品欧美 | 99在线精品视频 | 国产99久久 | 亚洲免费视频在线观看 | 国产小视频福利在线 | 免费三级a | 特级毛片在线观看 | 日韩中文字幕网站 | 丁香花在线观看视频在线 | 西西www444| 国产黄在线观看 | 成 人 黄 色 视频免费播放 | 91中文字幕在线观看 | 首页国产精品 | av资源网在线播放 | 五月婷激情 | 97在线免费观看 | 婷婷av资源 | 91视频高清免费 | 337p日本大胆噜噜噜噜 | 国产在线欧美在线 | 国产精品免费观看国产网曝瓜 | 国产日韩欧美精品在线观看 | 成全免费观看视频 | 特级西西444www大精品视频免费看 | 中文亚洲欧美日韩 | 麻豆国产精品永久免费视频 | 久久久久久免费网 | 成人黄色电影在线观看 | 国产精品高潮呻吟久久久久 | 99精品国产视频 | 在线观看中文字幕dvd播放 | 国产美女视频一区 | 久草电影免费在线观看 | 婷婷日日 | 久久成人高清 | 在线久久 | 久久久国产日韩 | 色999五月色 | 国产在线精品福利 | 悠悠av资源片 | 免费在线观看黄网站 | 天堂v中文 | 久久久毛片 | 婷婷网址 | 一级淫片a | 狠狠狠色丁香婷婷综合激情 | 欧美一区二区在线 | 日韩欧美精选 | 亚洲视频精选 | 国内精品视频久久 | 欧美一级免费高清 | 日日干 天天干 | 麻豆久久精品 | 亚洲日本韩国一区二区 | 国产护士av | 91av视频免费在线观看 | 在线看国产一区 | 黄色a一级视频 | 一级黄毛片 | 久久这里只有精品23 | 国产成人黄色片 | 激情网站免费观看 | 国产拍在线 | 日躁夜躁狠狠躁2001 | 色播激情五月 | 中文字幕在线免费看 | 免费网站污 | 毛片a级片| 国产一级视频在线观看 | 超碰在线观看99 | 国产不卡av在线 | 久草在线这里只有精品 | 午夜的福利 | 手机av在线网站 | 在线观看久草 | 色久网| 五月婷婷视频在线 | 欧美一级免费片 | 亚洲永久精品视频 | 日韩在线视频线视频免费网站 | 久久精品1区2区 | 国产最新精品视频 | 欧美一区二区在线刺激视频 | 国产一区福利在线 | 成人午夜在线观看 | 在线精品视频在线观看高清 | 日韩1级片 | 91精品亚洲影视在线观看 | 亚洲最新视频在线 | 国产在线1区 | 亚州精品在线视频 | 激情图片qvod | 国产成人精品午夜在线播放 | 激情婷婷在线 | 手机色在线 | 一区二区不卡在线观看 | 婷久久| 久久精品视频99 | 久久久亚洲成人 | 国产精品美女久久久久久久 | 国产一级免费播放 | 午夜性生活| 久久在线免费观看视频 | 日日操天天操狠狠操 | 人人爱人人添 | 日韩精品视频免费专区在线播放 | 狠狠干 狠狠操 | 精品一区av | 综合伊人久久 | av色综合网 | 国产h片在线观看 | 天天干天天操天天操 | 中文字幕制服丝袜av久久 | 日韩精品一区二区在线视频 | 黄色在线免费观看网址 | 激情在线五月天 | 三级av在线 | 伊甸园永久入口www 99热 精品在线 | 中文字幕在线视频网站 | 久久人人爽爽人人爽人人片av | 91av成人 | 亚洲午夜久久久影院 | 天天爱天天插 | 国产精品一区二区吃奶在线观看 | 又黄又刺激 | 91精品夜夜 | 久久艹精品 | 日韩精品大片 | 国产一区国产二区在线观看 | 激情网五月天 | 天天色棕合合合合合合 | 人人澡人人干 | 免费在线观看一级片 | 精品国产一区二区三区久久久蜜月 | 国产香蕉视频在线观看 | 在线视频a | 国产va精品免费观看 | 免费日韩 精品中文字幕视频在线 | 99产精品成人啪免费网站 | 日韩资源在线观看 | 91在线区 | 亚洲国产精品电影 | 久久久久久麻豆 | av一区二区在线观看中文字幕 | 日韩一区在线播放 | 在线视频欧美日韩 | 天天干天天干天天干天天干天天干天天干 | 国产精品二区三区 | 超碰在线观看97 | 97色视频在线 | 在线观看一级片 | 一区二区三区www | 九九热精品视频在线观看 | 99久久精品国产免费看不卡 | 69视频在线 | 亚洲一级二级 | 日本精品久久久一区二区三区 | 国产精品四虎 | 国产成人精品综合 | 色香蕉网| 久久视频 | 天天爽天天爽 | 色噜噜在线观看 | 精品久久久免费 | 国产成人精品亚洲 | 欧美国产日韩一区二区三区 | 婷婷色在线播放 | 超碰免费97 | 91人人干| 色老板在线视频 | 久免费 | 视频一区在线免费观看 | 免费看的黄色的网站 | 国产精国产精品 | 久久午夜影视 | 欧美日一级片 | av在线网站大全 | 成人性生交大片免费看中文网站 | 五月激情电影 | 九九热国产视频 | 久久视频一区 | 亚洲精品美女久久久久网站 | 国产人在线成免费视频 | 久久久久久久久久久高潮一区二区 | 麻豆国产精品一区二区三区 | 久久久黄色av | 91亚洲精品久久久中文字幕 | 亚洲精品福利在线观看 | 国产精品自产拍在线观看中文 | 久久久2o19精品 | 麻豆精品视频 | 欧美伦理电影一区二区 | 狠狠色伊人亚洲综合网站色 | 日本女人逼 | 91视频免费播放 | 久久精品99国产精品 | 久久字幕 | 9797在线看片亚洲精品 | 9色在线视频 | 日日干天天射 | 免费一级特黄毛大片 | 成年人视频在线免费 | 人人干人人做 | 一本一本久久a久久精品综合小说 | 91激情视频在线 | 亚洲激情六月 | 久久免费视频这里只有精品 | 国产小视频免费观看 | 成人超碰97 | 亚洲欧美视频在线观看 | 狠狠色伊人亚洲综合网站野外 | 视色网站 | 国产一级免费在线观看 | 欧美激情xxxx性bbbb | 亚洲黄色免费在线看 | 婷婷色综合| 国产成人一区二区三区影院在线 | 美女国内精品自产拍在线播放 | 久久综合天天 | 人成在线免费视频 | 国内精品小视频 | 91在线免费看片 | 欧美aaa大片 | 激情久久五月 | 四虎永久国产精品 | 天天骚夜夜操 | 国产精品电影一区 | 亚洲精品乱码白浆高清久久久久久 | 香蕉视频亚洲 | 国产精品免费在线 | 国产五月天婷婷 | 天天操夜夜爱 | 亚洲另类久久 | 日韩av男人的天堂 | 天天干天天干天天干天天干天天干天天干 | 精品免费久久久久久 | 久久五月婷婷丁香 | 日韩精品免费在线观看 | 免费久久99精品国产婷婷六月 | 亚洲狠狠操 | 欧美最猛性xxxxx免费 | 国产91在线播放 | 九九热在线观看 | 国产人成精品一区二区三 | 天天干 夜夜操 | 久草香蕉在线 | 精品久久久久久国产91 | 精品无人国产偷自产在线 | 久久久精品免费观看 | 亚洲国产精品久久久 | 亚洲精品人人 | 骄小bbw搡bbbb揉bbbb | 国产精品毛片久久蜜 | 日韩资源在线播放 | 黄色av电影网 | 久久免费中文视频 | 在线免费观看视频你懂的 | 国产精品对白一区二区三区 | avove黑丝 | 国产精品一区二区久久精品爱微奶 | 亚洲国产97在线精品一区 | 亚洲91在线| 日韩三级视频 | 亚洲在线视频观看 | 亚洲国产精品一区二区久久,亚洲午夜 | 国产美腿白丝袜足在线av | 午夜视频在线观看一区二区三区 | 国产一区二区精品91 | 五月综合色 | 99精品视频在线播放观看 | 国产午夜三级一区二区三桃花影视 | 五月婷婷一区二区三区 | 成人免费观看av | 成人毛片在线观看视频 | 国产日韩中文在线 | 国产亚洲人成网站在线观看 | 成人av亚洲 | 最近中文字幕完整高清 | 日韩av网页 | 九九亚洲精品 | 国产精品美女www爽爽爽视频 | 一级成人免费 | 午夜男人影院 | 91精品久久久久久综合乱菊 | 成人黄色资源 | 国产日韩视频在线播放 | 国产伦理精品一区二区 | 美女黄视频免费看 | 69热国产视频 | 一本一本久久aa综合精品 | 亚洲精品视频播放 | 免费一级特黄毛大片 | 国产永久免费高清在线观看视频 | 99精品久久久 | 亚洲精品777 | 日韩精品观看 | 天天摸天天操天天舔 | 91精品国产99久久久久久红楼 | 日韩精品一区二区在线观看视频 | 黄色免费网站 | 夜夜操天天操 | 日韩欧美精品在线视频 | 婷婷久操| 欧美精品首页 | 亚洲精品高清一区二区三区四区 | 午夜影视av | 91黄色免费网站 | 岛国精品一区二区 | 亚洲 欧美日韩 国产 中文 | 亚洲国产精品成人女人久久 | 亚洲精品av在线 | 国产又粗又长又硬免费视频 | 久久www免费视频 | 中文字幕在线看视频 | 国产美腿白丝袜足在线av | 亚洲国产字幕 | 亚洲四虎影院 | 国产精品免费大片视频 | 国产成人一级电影 | 丁香花中文在线免费观看 | 久久免费在线视频 | 色www精品视频在线观看 | 天天爽天天碰狠狠添 | 91中文字幕在线视频 | 免费日p视频 | 国语自产偷拍精品视频偷 | 精品久久久久久久久久久久久久久久 | 亚洲精品综合在线观看 | 69视频国产| 麻豆 free xxxx movies hd | 极品国产91在线网站 | 精品国产免费看 | 欧美91精品久久久久国产性生爱 | 欧美日韩国产二区三区 | 久久精品国产精品亚洲精品 | 中文字幕丰满人伦在线 | 亚洲精品视频在线免费 | 欧美黄污视频 | 91麻豆精品国产自产在线 | 99精品国产福利在线观看免费 | 欧美性爽爽| 国产精品久久久久久麻豆一区 | 亚洲一区二区三区四区在线视频 | 欧美日韩一级在线 | 欧美日本三级 | 日p视频在线观看 | 久久久久亚洲精品男人的天堂 | 天天干天天插 | 国产精品一区二区62 | 在线亚洲成人 | 欧美日韩中文字幕综合视频 | 国产一区视频在线 | 久久精品五月 | 在线黄色免费 | 日韩素人在线观看 | 久草在线观看资源 | 日本黄色免费网站 | 欧美伦理一区二区三区 | 91av影视| 色综合久久久久久久久五月 | 国产一区二区三区午夜 | 九九热只有精品 | 亚洲精品白浆高清久久久久久 | 久久草网 | 亚洲最新视频在线播放 | 岛国av在线不卡 | 欧美色图狠狠干 | 夜夜躁天天躁很躁波 | 在线电影a | 97精品国自产拍在线观看 | 激情综合站 | 性色av一区二区三区在线观看 | 婷婷综合电影 | 在线va网站 | 国产精品区二区三区日本 | 六月丁香婷婷久久 | 91九色蝌蚪在线 | 日日插日日干 | av电影一区 | 久久99久久久久久 | 国产精品完整版 | 99免在线观看免费视频高清 | ww亚洲ww亚在线观看 | 欧美精品中文 | 在线 高清 中文字幕 | 超碰97网站 | 久久久久国产精品厨房 | 国产亚洲日本 | 亚洲作爱| 午夜精品一区二区国产 | 最新日本中文字幕 | 免费手机黄色网址 | 精品视频中文字幕 | 一级黄色在线免费观看 | 久久96国产精品久久99漫画 | 欧美一级久久 | 亚洲欧美精品一区二区 | 久久免费看毛片 | 综合久久久久久久久 | 成人观看视频 | 婷婷色影院 | 在线免费观看黄色 | 久久久免费观看完整版 | 青青久草在线视频 | 波多野结衣视频一区二区 | 天海翼一区二区三区免费 | 一区 二区电影免费在线观看 | 色偷偷男人的天堂av | av青草 | 欧美特一级 | 国产精品久久久久久久久久久免费看 | 久久免费在线观看视频 | 国产精品男女 | 综合网av| 全久久久久久久久久久电影 | 狠狠狠狠狠狠狠狠 | 国产精品99蜜臀久久不卡二区 | 成年人黄色大片在线 | 精品视频成人 | 成人黄在线观看 | 日本婷婷色 | 狠狠躁夜夜躁人人爽超碰91 | 91福利社在线观看 | 天天干天天色2020 | 日韩欧美69 | 久精品视频在线 | 麻豆国产视频 | 黄色片网站大全 | 国产一在线精品一区在线观看 | 欧美日韩高清在线观看 | 97久久久免费福利网址 | 五月婷婷操 | 久久黄色网页 | 男女免费视频观看 | 99热国产在线观看 | 国产精品麻豆三级一区视频 | 中国美女一级看片 | 特片网久久 | 免费看一级特黄a大片 | 在线 高清 中文字幕 | 不卡日韩av | 日韩欧美在线免费观看 | 最新黄色av网址 | av一区二区在线观看中文字幕 | 日韩色在线观看 | 久久撸在线视频 | 亚洲成人欧美 | 国产精国产精品 | 亚洲国产精品500在线观看 | 一级黄色电影网站 | 91免费在线播放 | 黄色www在线观看 | 91久久精品一区二区三区 | 精品欧美日韩 | 欧美福利在线播放 | 激情五月视频 | 婷婷五月色综合 | 免费在线观看一区 | 五月激情av| 99久久久久国产精品免费 | 久久精视频 | www.99av| 久久精品播放 | 色av资源网 | 日韩av高清在线观看 | 欧美性成人 | 久久久久色 | 国产三级在线播放 | 日本在线观看一区二区 | 亚洲无吗天堂 | 五月天久久久久久 | 日韩欧美在线视频一区二区 | 欧美精品一区在线 | 色夜视频 | 波多野结衣一区二区三区中文字幕 | 99久久er热在这里只有精品15 | 日韩精品视 | 国产精品大全 | 精品久久久久久久久久 | 日韩动漫免费观看高清完整版在线观看 | 国内精品久久久久 | 日本精品视频在线播放 | 中文字幕成人一区 | 国产资源免费在线观看 | 不卡电影免费在线播放一区 | 超碰97人 | 中文字幕在线色 | 欧美极品xxxx| 在线免费观看欧美日韩 | 综合久色| 五月天视频网站 | 欧美一级视频免费 | 久日精品| 日批视频| 五月天天色 | 青青河边草免费 | 人人舔人人 | 天天操网站 | 中文字幕亚洲精品在线观看 | 色偷偷97| 在线观看国产福利片 | 亚洲精品久久久久www | 中文字幕免费高清 | 国内丰满少妇猛烈精品播放 | 88av网站| 在线一二三四区 |