• <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>
            posts - 297,  comments - 15,  trackbacks - 0
            本文介紹Linux系統(tǒng)網(wǎng)絡(luò)編程的內(nèi)容,如套接口的概念與使用、網(wǎng)絡(luò)編程的結(jié)構(gòu)等。
            1 什么是套接口
            簡(jiǎn)單地說,套接口就是一種使用UNIX系統(tǒng)中的文件描述符和系統(tǒng)進(jìn)程通信的一種方法。
            因?yàn)樵赨NIX系統(tǒng)中,所有的I/O操作都是通過讀寫文件描述符而產(chǎn)生的。文件描述符就是
            一個(gè)和打開的文件相關(guān)連的整數(shù)。但文件可以是一個(gè)網(wǎng)絡(luò)連接、一個(gè)FIFO、一個(gè)管道、一個(gè)
            終端、一個(gè)真正存儲(chǔ)在磁盤上的文件或者UNIX系統(tǒng)中的任何其他的東西。所以,如果你希望
            通過Internet和其他的程序進(jìn)行通信,你只有通過文件描述符。
            使用系統(tǒng)調(diào)用socket(),你可以得到socket()描述符。然后你可以使用send() 和recv()調(diào)用而
            與其他的程序通信。你也可以使用一般的文件操作來調(diào)用read() 和write()而與其他的程序進(jìn)行
            通信,但send() 和recv()調(diào)用可以提供一種更好的數(shù)據(jù)通信的控制手段。下面我們討論Internet
            套接口的使用方法。
            2 兩種類型的Internet套接口
            有兩種最常用的Internet 套接口,“數(shù)據(jù)流套接口”和“數(shù)據(jù)報(bào)套接口”,以后我們用
            “SOCK_STREAM” 和“SOCK_DGRAM”分別代表上面兩種套接口。數(shù)據(jù)報(bào)套接口有時(shí)也
            叫做“無連接的套接口”。
            數(shù)據(jù)流套接口是可靠的雙向連接的通信數(shù)據(jù)流。如果你在套接口中以“ 1, 2”的順序放入
            兩個(gè)數(shù)據(jù),它們?cè)诹硪欢艘矔?huì)以“1, 2”的順序到達(dá)。它們也可以被認(rèn)為是無錯(cuò)誤的傳輸。
            經(jīng)常使用的telnet應(yīng)用程序就是使用數(shù)據(jù)流套接口的一個(gè)例子。使用HTTP的WWW瀏覽器
            也使用數(shù)據(jù)流套接口來讀取網(wǎng)頁。事實(shí)上,如果你使用telnet 登錄到一個(gè)WWW站點(diǎn)的8 0端口,
            然后鍵入“GET 網(wǎng)頁名”,你將可以得到這個(gè)HTML頁。數(shù)據(jù)流套接口使用TCP得到這種高質(zhì)
            量的數(shù)據(jù)傳輸。數(shù)據(jù)報(bào)套接口使用UDP,所以數(shù)據(jù)報(bào)的順序是沒有保障的。數(shù)據(jù)報(bào)是按一種應(yīng)
            答的方式進(jìn)行數(shù)據(jù)傳輸?shù)摹?br>3 網(wǎng)絡(luò)協(xié)議分層
            由于網(wǎng)絡(luò)中的協(xié)議是分層的,所以上層的協(xié)議是依賴于下一層所提供的服務(wù)的。也就是說,
            你可以在不同的物理網(wǎng)絡(luò)中使用同樣的套接口程序,因?yàn)橄聦拥膮f(xié)議對(duì)你來說是透明的。
            UNIX系統(tǒng)中的網(wǎng)絡(luò)協(xié)議是這樣分層的:
            • 應(yīng)用層( telnet、ftp等)。
            • 主機(jī)到主機(jī)傳輸層( TCP、UDP )。
            • Internet層( IP和路由)。
            • 網(wǎng)絡(luò)訪問層(網(wǎng)絡(luò)、數(shù)據(jù)鏈路和物理層)。
            4 數(shù)據(jù)結(jié)構(gòu)
            下面我們要討論使用套接口編寫程序可能要用到的數(shù)據(jù)結(jié)構(gòu)。
            首先是套接口描述符。一個(gè)套接口描述符只是一個(gè)整型的數(shù)值: i n t。
            第一個(gè)數(shù)據(jù)結(jié)構(gòu)是struct sockaddr,這個(gè)數(shù)據(jù)結(jié)構(gòu)中保存著套接口的地址信息。
            struct sockaddr {
               unsigned short sa_family;    /* address family, AF_xxx */
               char sa_data[14];               /* 14 bytes of protocol address */
            } ;
            sa_family 中可以是其他的很多值,但在這里我們把它賦值為“ A F _ I N E T”。s a _ d a t a包括一
            個(gè)目的地址和一個(gè)端口地址。
            你也可以使用另一個(gè)數(shù)據(jù)結(jié)構(gòu)s o c k a d d r _ i n,如下所示:
            struct sockaddr_in {
               short int sin_family;                   /* Address family */
               unsigned short int sin_port;       /* Port number */
               struct in_addr sin_addr;           /* Internet address */
               unsigned char sin_zero[8];       /* Same size as struct sockaddr */
            } ;
            這個(gè)數(shù)據(jù)結(jié)構(gòu)使得使用其中的各個(gè)元素更為方便。要注意的是sin_zero應(yīng)該使用bzero() 或
            者memset()而設(shè)置為全0。另外,一個(gè)指向sockaddr_in數(shù)據(jù)結(jié)構(gòu)的指針可以投射到一個(gè)指向數(shù)
            據(jù)結(jié)構(gòu)sockaddr的指針,反之亦然。
            5 IP地址和如何使用IP地址
            有一系列的程序可以使你處理I P地址。
            首先,你可以使用inet_addr()程序把諸如“132.241.5.10”形式的IP地址轉(zhuǎn)化為無符號(hào)的整
            型數(shù)。
            ina.sin_addr.s_addr = inet_addr("132.241.5.10");
            如果出錯(cuò),inet_addr()程序?qū)⒎祷? 1。
            也可以調(diào)用inet_ntoa()把地址轉(zhuǎn)換成數(shù)字和句點(diǎn)的形式:
            printf ("%s", inet_ntoa (ina.sin_addr));
            這將會(huì)打印出I P地址。它返回的是一個(gè)指向字符串的指針。
            5.1 socket()
            我們使用系統(tǒng)調(diào)用socket()來獲得文件描述符:
            #include <sys/types.h>
            #include <sys/socket.h>
            int socket(int domain, int type, int protocol);
            第一個(gè)參數(shù)domain設(shè)置為“AF_INET”。第二個(gè)參數(shù)是套接口的類型:SOCK_STREAM 或
            SOCK_DGRAM。第三個(gè)參數(shù)設(shè)置為0。
            系統(tǒng)調(diào)用socket()只返回一個(gè)套接口描述符,如果出錯(cuò),則返回- 1。
            5.2 bind()
            一旦你有了一個(gè)套接口以后,下一步就是把套接口綁定到本地計(jì)算機(jī)的某一個(gè)端口上。但
            如果你只想使用connect()則無此必要。
            下面是系統(tǒng)調(diào)用bind()的使用方法:
            #include <sys/types.h>
            #include <sys/socket.h>
            int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
            第一個(gè)參數(shù)sockfd 是由socket()調(diào)用返回的套接口文件描述符。第二個(gè)參數(shù)my_addr 是指向
            數(shù)據(jù)結(jié)構(gòu)sockaddr的指針。數(shù)據(jù)結(jié)構(gòu)sockaddr中包括了關(guān)于你的地址、端口和I P地址的信息。
            第三個(gè)參數(shù)addrlen可以設(shè)置成sizeof(struct sockaddr)。
            下面是一個(gè)例子:
            #include <string.h>
            #include <sys/types.h>
            #include <sys/socket.h>
            #define MYPORT 3490
            m a i n ( )
            {
            int sockfd;
            struct sockaddr_in my_addr;
            sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */
            m y _ a d d r.sin_family = AF_INET; /* host byte order */
            m y _ a d d r.sin_port = htons(MYPORT); /* short, network byte order */
            m y _ a d d r. s i n _ a d d r.s_addr = inet_addr("132.241.5.10");
            b z e r o ( & ( m y _ a d d r.sin_zero), 8); /* zero the rest of the struct */
            /* don't forget your error checking for bind(): */
            bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
            如果出錯(cuò),bind() 也返回- 1。
            如果你使用connect()系統(tǒng)調(diào)用,那么你不必知道你使用的端口號(hào)。當(dāng)你調(diào)用connect()時(shí),
            它檢查套接口是否已經(jīng)綁定,如果沒有,它將會(huì)分配一個(gè)空閑的端口。
            5.3 connect()
            系統(tǒng)調(diào)用connect()的用法如下:
            #include <sys/types.h>
            #include <sys/socket.h>
            int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
            第一個(gè)參數(shù)還是套接口文件描述符,它是由系統(tǒng)調(diào)用socket()返回的。第二個(gè)參數(shù)是
            serv_addr是指向數(shù)據(jù)結(jié)構(gòu)sockaddr的指針,其中包括目的端口和I P地址。第三個(gè)參數(shù)可以使用
            sizeof(struct sockaddr)而獲得。下面是一個(gè)例子:
            #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; /* will hold the destination addr */
            sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */
            d e s t _ a d d r.sin_family = AF_INET; /* host byte order */
            d e s t _ a d d r.sin_port = htons(DEST_PORT); /* short, network byte order */
            d e s t _ a d d r. s i n _ a d d r.s_addr = inet_addr(DEST_IP);
            b z e r o ( & ( d e s t _ a d d r.sin_zero), 8); /* zero the rest of the struct */
            /* don't forget to error check the connect()! */
            connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
            同樣,如果出錯(cuò), c o n n e c t ( )將會(huì)返回- 1。
            5.4 listen()
            如果你希望不連接到遠(yuǎn)程的主機(jī),也就是說你希望等待一個(gè)進(jìn)入的連接請(qǐng)求,然后再處理
            它們。這樣,你通過首先調(diào)用l i s t e n ( ),然后再調(diào)用a c c e p t ( )來實(shí)現(xiàn)。
            系統(tǒng)調(diào)用l i s t e n ( )的形式如下:
            int listen(int sockfd, int backlog);
            第一個(gè)參數(shù)是系統(tǒng)調(diào)用s o c k e t ( )返回的套接口文件描述符。第二個(gè)參數(shù)是進(jìn)入隊(duì)列中允許
            的連接的個(gè)數(shù)。進(jìn)入的連接請(qǐng)求在使用系統(tǒng)調(diào)用a c c e p t ( )應(yīng)答之前要在進(jìn)入隊(duì)列中等待。這個(gè)
            值是隊(duì)列中最多可以擁有的請(qǐng)求的個(gè)數(shù)。大多數(shù)系統(tǒng)的缺省設(shè)置為2 0。你可以設(shè)置為5或者1 0。
            當(dāng)出錯(cuò)時(shí),l i s t e n ( )將會(huì)返回- 1值。
            當(dāng)然,在使用系統(tǒng)調(diào)用l i s t e n ( )之前,我們需要調(diào)用b i n d ( )綁定到需要的端口,否則系統(tǒng)內(nèi)
            核將會(huì)讓我們監(jiān)聽一個(gè)隨機(jī)的端口。所以,如果你希望監(jiān)聽一個(gè)端口,下面是應(yīng)該使用的系統(tǒng)
            調(diào)用的順序:
            s o c k e t ( ) ;
            b i n d ( ) ;
            l i s t e n ( ) ;
            /* accept() goes here */
            5.5 accept()
            系統(tǒng)調(diào)用a c c e p t ( )比較起來有點(diǎn)復(fù)雜。在遠(yuǎn)程的主機(jī)可能試圖使用c o n n e c t ( )連接你使用
            l i s t e n ( )正在監(jiān)聽的端口。但此連接將會(huì)在隊(duì)列中等待,直到使用a c c e p t ( )處理它。調(diào)用a c c e p t ( )
            之后,將會(huì)返回一個(gè)全新的套接口文件描述符來處理這個(gè)單個(gè)的連接。這樣,對(duì)于同一個(gè)連接
            來說,你就有了兩個(gè)文件描述符。原先的一個(gè)文件描述符正在監(jiān)聽你指定的端口,新的文件描
            述符可以用來調(diào)用s e n d ( )和r e c v ( )。
            調(diào)用的例子如下:
            #include <sys/socket.h>
            int accept(int sockfd, void *addr, int *addrlen);
            第一個(gè)參數(shù)是正在監(jiān)聽端口的套接口文件描述符。第二個(gè)參數(shù)a d d r是指向本地的數(shù)據(jù)結(jié)構(gòu)
            s o c k a d d r _ i n的指針。調(diào)用c o n n e c t ( )中的信息將存儲(chǔ)在這里。通過它你可以了解哪個(gè)主機(jī)在哪個(gè)
            端口呼叫你。第三個(gè)參數(shù)同樣可以使用sizeof(struct sockaddr_in)來獲得。
            如果出錯(cuò),a c c e p t ( )也將返回- 1。下面是一個(gè)簡(jiǎn)單的例子:
            #include <string.h>
            #include <sys/types.h>
            #include <sys/socket.h>
            #define MYPORT 3490 /* the port users will be connecting to */
            #define BACKLOG 10 /* how many pending connections queue will hold */
            m a i n ( )
            {
            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;
            sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */
            m y _ a d d r.sin_family = AF_INET; /* host byte order */
            m y _ a d d r.sin_port = htons(MYPORT); /* short, network byte order */
            m y _ a d d r. s i n _ a d d r.s_addr = INADDR_ANY; /* auto-fill with my IP */
            b z e r o ( & ( m y _ a d d r.sin_zero), 8); /* 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);
            下面,我們將可以使用新創(chuàng)建的套接口文件描述符n e w _ f d來調(diào)用s e n d ( )和r e c v ( )。
            5.6 send() 和recv()
            系統(tǒng)調(diào)用s e n d ( )的用法如下:
            int send(int sockfd, const void *msg, int len, int flags);
            第一個(gè)參數(shù)是你希望給發(fā)送數(shù)據(jù)的套接口文件描述符。它可以是你通過s o c k e t ( )系統(tǒng)調(diào)用
            返回的,也可以是通過a c c e p t ( )系統(tǒng)調(diào)用得到的。第二個(gè)參數(shù)是指向你希望發(fā)送的數(shù)據(jù)的指針。
            第三個(gè)參數(shù)是數(shù)據(jù)的字節(jié)長(zhǎng)度。第四個(gè)參數(shù)標(biāo)志設(shè)置為0。
            下面是一個(gè)簡(jiǎn)單的例子:
            char *msg = "Beej was here!";
            int len, bytes_sent;
            len = strlen(msg);
            bytes_sent = send(sockfd, msg, len, 0);
            系統(tǒng)調(diào)用s e n d ( )返回實(shí)際發(fā)送的字節(jié)數(shù),這可能比你實(shí)際想要發(fā)送的字節(jié)數(shù)少。如果返回
            的字節(jié)數(shù)比要發(fā)送的字節(jié)數(shù)少,你在以后必須發(fā)送剩下的數(shù)據(jù)。當(dāng)s e n d ( )出錯(cuò)時(shí),將返回- 1。
            系統(tǒng)調(diào)用r e c v ( )的使用方法和s e n d ( )類似:
            int recv(int sockfd, void *buf, int len, unsigned int flags);
            第一個(gè)參數(shù)是要讀取的套接口文件描述符。第二個(gè)參數(shù)是保存讀入信息的地址。第三個(gè)參
            數(shù)是緩沖區(qū)的最大長(zhǎng)度。第四個(gè)參數(shù)設(shè)置為0。
            系統(tǒng)調(diào)用r e c v ( )返回實(shí)際讀取到緩沖區(qū)的字節(jié)數(shù),如果出錯(cuò)則返回- 1。
            這樣使用上面的系統(tǒng)調(diào)用,你可以通過數(shù)據(jù)流套接口來發(fā)送和接受信息。
            5.7 sendto() 和recvfrom()
            因?yàn)閿?shù)據(jù)報(bào)套接口并不連接到遠(yuǎn)程的主機(jī)上,所以在發(fā)送數(shù)據(jù)包之前,我們必須首先給出
            目的地址,請(qǐng)看:
            int sendto(int sockfd, const void *msg, int len, unsigned int flags,
            const struct sockaddr *to, int tolen);
            除了兩個(gè)參數(shù)以外,其他的參數(shù)和系統(tǒng)調(diào)用s e n d ( )時(shí)相同。參數(shù)t o是指向包含目的I P地址
            和端口號(hào)的數(shù)據(jù)結(jié)構(gòu)s o c k a d d r的指針。參數(shù)t o l e n可以設(shè)置為sizeof(struct sockaddr)。
            系統(tǒng)調(diào)用s e n d t o ( )返回實(shí)際發(fā)送的字節(jié)數(shù),如果出錯(cuò)則返回- 1。
            系統(tǒng)調(diào)用r e c v f r o m ( )的使用方法也和r e c v ( )的十分近似:
            int recvfrom(int sockfd, void *buf, int len, unsigned int flags
            struct sockaddr *from, int *fromlen);
            參數(shù)f r o m是指向本地計(jì)算機(jī)中包含源I P地址和端口號(hào)的數(shù)據(jù)結(jié)構(gòu)s o c k a d d r的指針。參數(shù)
            f r o m l e n設(shè)置為sizeof(struct sockaddr)。
            系統(tǒng)調(diào)用r e c v f r o m ( )返回接收到的字節(jié)數(shù),如果出錯(cuò)則返回- 1。
            5.8 close() 和shutdown()
            你可以使用c l o s e ( )調(diào)用關(guān)閉連接的套接口文件描述符:
            c l o s e ( s o c k f d ) ;
            這樣就不能再對(duì)此套接口做任何的讀寫操作了。
            使用系統(tǒng)調(diào)用s h u t d o w n ( ),可有更多的控制權(quán)。它允許你在某一個(gè)方向切斷通信,或者切
            斷雙方的通信:
            int shutdown(int sockfd, int how);
            第一個(gè)參數(shù)是你希望切斷通信的套接口文件描述符。第二個(gè)參數(shù)h o w值如下:
            0—Further receives are disallowed
            1—Further sends are disallowed
            2—Further sends and receives are disallowed (like close())
            shutdown() 如果成功則返回0,如果失敗則返回- 1。
            5.9 getpeername()
            這個(gè)系統(tǒng)的調(diào)用十分簡(jiǎn)單。它將告訴你是誰在連接的另一端:
            #include <sys/socket.h>
            int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
            第一個(gè)參數(shù)是連接的數(shù)據(jù)流套接口文件描述符。第二個(gè)參數(shù)是指向包含另一端的信息的數(shù)
            據(jù)結(jié)構(gòu)s o c k a d d r的指針。第三個(gè)參數(shù)可以設(shè)置為sizeof(struct sockaddr)。
            如果出錯(cuò),系統(tǒng)調(diào)用將返回- 1。
            一旦你獲得了它們的地址,你可以使用inet_ntoa() 或者g e t h o s t b y a d d r ( )來得到更多的信息。
            5.10 gethostname()
            系統(tǒng)調(diào)用g e t h o s t n a m e ( )比系統(tǒng)調(diào)用g e t p e e r n a m e ( )還簡(jiǎn)單。它返回程序正在運(yùn)行的計(jì)算機(jī)的
            名字。系統(tǒng)調(diào)用g e t h o s t b y n a m e ( )可以使用這個(gè)名字來決定你的機(jī)器的I P地址。
            下面是一個(gè)例子:
            #include <unistd.h>
            int gethostname(char *hostname, size_t size);
            如果成功,g e t h o s t n a m e將返回0。如果失敗,它將返回- 1。
            6 DNS
            DNS 代表“Domain Name Service”,即域名服務(wù)器。它可以把域名翻譯成相應(yīng)的I P地址。
            你可以使用此I P地址調(diào)用b i n d ( )、c o n n e c t ( )、s e n d t o ( )或者用于其他的地方。
            系統(tǒng)調(diào)用g e t h o s t b y n a m e ( )可以完成這個(gè)函數(shù):
            #include <netdb.h>
            struct hostent *gethostbyname(const char *name);
            它返回一個(gè)指向數(shù)據(jù)結(jié)構(gòu)h o s t e n t的指針,數(shù)據(jù)結(jié)構(gòu)h o s t e n t如下:
            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]
            h_name —主機(jī)的正式名稱。
            h_aliases —主機(jī)的別名。
            h _ a d d r t y p e—將要返回的地址的類型,一般是A F _ I N E T。
            h _ l e n g t h—地址的字節(jié)長(zhǎng)度。
            h _ a d d r _ l i s t—主機(jī)的網(wǎng)絡(luò)地址。
            h_addr —h _ a d d r _ l i s t中的第一個(gè)地址。
            系統(tǒng)調(diào)用g e t h o s t b y n a m e ( )返回一個(gè)指向填充好的數(shù)據(jù)結(jié)構(gòu)h o s t e n t的指針。當(dāng)發(fā)生錯(cuò)誤時(shí),
            則返回一個(gè)N U L L指針。下面是一個(gè)實(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) { /* error check the command line */
            f p r i n t f ( s t d e r r,"usage: getip address\n");
            e x i t ( 1 ) ;
            }
            if ((h=gethostbyname(argv[1])) == NULL) { /* get the host info */
            h e r r o r ( " g e t h o s t b y n a m e " ) ;
            e x i t ( 1 ) ;
            }
            printf("Host name : %s\n", h->h_name);
            printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));
            return 0;
            }
            在使用g e t h o s t b y n a m e ( )時(shí),你不能使用p e r r o r ( )來打印錯(cuò)誤信息。你應(yīng)該使用的是系統(tǒng)調(diào)用
            h e r r o r ( )。
            7 客戶機(jī)/服務(wù)器模式
            在網(wǎng)絡(luò)上大部分的通信都是在客戶機(jī)/服務(wù)器模式下進(jìn)行的。例如t e l n e t。當(dāng)你使用t e l n e t連
            接到遠(yuǎn)程主機(jī)的端口2 3時(shí),主機(jī)上的一個(gè)叫做t e l n e t d的程序就開始運(yùn)行。它處理所有進(jìn)入的
            t e l n e t連接,為你設(shè)置登錄提示符等。
            應(yīng)當(dāng)注意的是客戶機(jī)/服務(wù)器模式可以使用S O C K _ S T R E A M、S O C K _ D G R A M或者任何其
            他的方式。例如t e l n e t / t e l n e t d、f t p / f t p d和b o o t p / b o o t p d。每當(dāng)你使用f t p時(shí),遠(yuǎn)程計(jì)算機(jī)都在運(yùn)
            行一個(gè)f t p d為你服務(wù)。
            一般情況下,一臺(tái)機(jī)器上只有一個(gè)服務(wù)器程序,它通過使用f o r k ( )來處理多個(gè)客戶端程序
            的請(qǐng)求。最基本的處理方法是:服務(wù)器等待連接,使用a c c e p t ( )接受連接,調(diào)用f o r k ( )生成一個(gè)
            子進(jìn)程處理連接。
            8 簡(jiǎn)單的數(shù)據(jù)流服務(wù)器程序
            此服務(wù)器程序所作的事情就是通過一個(gè)數(shù)據(jù)流連接發(fā)送字符串“ Hello, Wo r l d ! \ n”。你可以
            在一個(gè)窗口上運(yùn)行此程序,然后在另一個(gè)窗口使用t e l n e t:
            $ telnet remotehostname 3490
            其中,r e m o t e h o s t n a m e是你運(yùn)行的機(jī)器名。下面是此程序的代碼:
            #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 /* the port users will be connecting to */
            #define BACKLOG 10 /* how many pending connections queue will hold */
            m a i n ( )
            {
            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) {
            p e r r o r ( " s o c k e t " ) ;
            e x i t ( 1 ) ;
            }
            m y _ a d d r.sin_family = AF_INET; /* host byte order */
            m y _ a d d r.sin_port = htons(MYPORT); /* short, network byte order */
            m y _ a d d r. s i n _ a d d r.s_addr = INADDR_ANY; /* auto-fill with my IP */
            b z e r o ( & ( m y _ a d d r.sin_zero), 8); /* zero the rest of the struct */
            if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \
            == -1) {
            p e r r o r ( " b i n d " ) ;
            e x i t ( 1 ) ;
            }
            if (listen(sockfd, BACKLOG) == -1) {
            p e r r o r ( " l i s t e n " ) ;
            e x i t ( 1 ) ;
            }
            while(1) { /* main accept() loop */
            sin_size = sizeof(struct sockaddr_in);
            if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \
            &sin_size)) == -1) {
            p e r r o r ( " a c c e p t " ) ;
            c o n t i n u e ;
            }
            printf("server: got connection from %s\n", \
            i n e t _ n t o a ( t h e i r _ a d d r. s i n _ a d d r ) ) ;
            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 */
            }
            }
            你也可以使用下面的客戶機(jī)程序從服務(wù)器上得到字符串。
            9 簡(jiǎn)單的數(shù)據(jù)流客戶機(jī)程序
            客戶機(jī)所做的是連接到你在命令行中指定的主機(jī)的3 4 9 0端口。它讀取服務(wù)器發(fā)送的字符
            串。
            下面是客戶機(jī)程序的代碼:
            #include <stdio.h>
            #include <stdlib.h>
            #include <errno.h>
            #include <string.h>
            #include <netdb.h>
            #include <sys/types.h>
            #include <netinet/in.h>
            #include <sys/socket.h>
            #define PORT 3490 /* the port client will be connecting to */
            #define MAXDATASIZE 100 /* max number of bytes we can get at once */
            int main(int argc, char *argv[])
            {
            int sockfd, numbytes;
            char buf[MAXDATA S I Z E ] ;
            struct hostent *he;
            struct sockaddr_in their_addr; /* connector's address information */
            if (argc != 2) {
            f p r i n t f ( s t d e r r,"usage: client hostname\n");
            e x i t ( 1 ) ;
            }
            if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
            h e r r o r ( " g e t h o s t b y n a m e " ) ;
            e x i t ( 1 ) ;
            }
            if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            p e r r o r ( " s o c k e t " ) ;
            e x i t ( 1 ) ;
            }
            t h e i r _ a d d r.sin_family = AF_INET; /* host byte order */
            t h e i r _ a d d r.sin_port = htons(PORT); /* short, network byte order */
            t h e i r _ a d d r.sin_addr = *((struct in_addr *)he->h_addr);
            b z e r o ( & ( t h e i r _ a d d r.sin_zero), 8); /* zero the rest of the struct */
            if (connect(sockfd, (struct sockaddr *)&their_addr, \
            sizeof(struct sockaddr)) == -1) {
            p e r r o r ( " c o n n e c t " ) ;
            e x i t ( 1 ) ;
            }
            if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
            p e r r o r ( " r e c v " ) ;
            e x i t ( 1 ) ;
            }
            buf[numbytes] = '\0';
            printf("Received: %s",buf);
            c l o s e ( s o c k f d ) ;
            return 0;
            }
            如果你在運(yùn)行服務(wù)器程序之前運(yùn)行客戶機(jī)程序,則將會(huì)得到一個(gè)“ Connection refused”的
            信息。
            10 數(shù)據(jù)報(bào)套接口
            程序l i s t e n e r在機(jī)器中等待端口4 9 5 0到來的數(shù)據(jù)包。程序t a l k e r向指定的機(jī)器的4 9 5 0端口發(fā)
            送數(shù)據(jù)包。
            下面是l i s t e n e r. 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
            m a i n ( )
            {
            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_a d d r.s i n_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
            bzero(&(my_addr.sin_zero), 8); /* 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 %s\n",inet_ntoa(their_addr. s i n _ a d d r ) ) ;
            printf("packet is %d bytes long\n",numbytes);
            buf[numbytes] = '\0';
            printf("packet contains \"%s\"\n",buf);
            close(sockfd);
            }
            下面是t a l k e r. c的代碼:
            #include <stdio.h>
            #include <stdlib.h>
            #include <errno.h>
            #include <string.h>
            #include <sys/types.h>
            #include <netinet/in.h>
            #include <netdb.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 message\n");
            exit(1);
            }
            if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
            h e r r o r ( " g e t h o s t b y n a m e " ) ;
            e x i t ( 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), 8); /* 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 ("s e n d t o");
            exit(1);
            }
            printf("sent %d bytes to %s\n",numbytes,inet_ntoa(their_addr. s i n _ a d d r ) ) ;
            close(sockfd);
            return 0;
            }
            你可以在一臺(tái)機(jī)器上運(yùn)行l(wèi) i s t e n e r程序,在另一臺(tái)機(jī)器上運(yùn)行t a l k e r程序,然后觀察它們之
            間的通信。
            21.11 阻塞
            當(dāng)使用上面的listener程序時(shí),此程序在等待直到一個(gè)數(shù)據(jù)包到來。這是因?yàn)樗{(diào)用了
            recvform(),如果沒有數(shù)據(jù),recvform ( )就一直阻塞,直到有數(shù)據(jù)到來。
            很多函數(shù)都有阻塞。系統(tǒng)調(diào)用accept ( )阻塞,所有的類似recv*()的函數(shù)也可以阻塞。它們
            之所以可以阻塞是因?yàn)橄到y(tǒng)內(nèi)核允許它們阻塞。當(dāng)你第一次創(chuàng)建一個(gè)套接口文件描述符時(shí),系
            統(tǒng)內(nèi)核將它設(shè)置為可以阻塞。如果你不希望套接口阻塞,你可以使用系統(tǒng)調(diào)用fcntl():
            #include <unistd.h>
            #include <fcntl.h>
            .
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            fcntl(sockfd, F_SETFL, O_NONBLOCK);
            .
            如果你設(shè)置為不阻塞,那么就得頻繁地詢問套接口以便檢查有無信息到來。如果你試圖讀
            取一個(gè)沒有阻塞的套接口,同時(shí)它又沒有數(shù)據(jù),那么你將得到- 1。
            詢問套接口以檢查有無信息得到來可能會(huì)占用太多的C P U時(shí)間。另一個(gè)可以使用的方法是
            select()。
            select()用于同步I / O多路復(fù)用。這個(gè)系統(tǒng)調(diào)用十分有用。考慮一下下面的情況:你是一個(gè)
            服務(wù)器,你希望監(jiān)聽進(jìn)入的連接,同時(shí)還一直從已有的連接中讀取信息。
            也許你認(rèn)為可以使用一個(gè)accept()調(diào)用和幾個(gè)recv()調(diào)用。但如果調(diào)用accept()阻塞了怎么
            辦?如果在這時(shí)你希望調(diào)用recv()接受數(shù)據(jù)呢?
            系統(tǒng)調(diào)用select()使得你可以同時(shí)監(jiān)視幾個(gè)套接口。它可以告訴你哪一個(gè)套接口已經(jīng)準(zhǔn)備好
            了以供讀取,哪一個(gè)套接口已經(jīng)可以寫入。
            下面是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)視幾個(gè)文件描述符,特別是r e a d f d s、w r i t e f d s和e x c e p t f d s。如果你希望檢查是否
            可以從標(biāo)準(zhǔn)輸入中和一些其他的套接口文件描述符s o c k f d中讀取數(shù)據(jù),只需把文件描述符0和
            s o c k f d添加到r e a d f d s中。參數(shù)n u m f d s應(yīng)該設(shè)置為最高的文件描述符的值加1。
            當(dāng)s e l e c t ( )返回時(shí),r e a d f d s將會(huì)被修改以便反映你選擇的那一個(gè)文件描述符已經(jīng)準(zhǔn)備好了以
            供讀取。你可以使用F D _ I S S E T ( )測(cè)試。
            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)—檢測(cè)fd 是否在文件描述符集中。
            數(shù)據(jù)結(jié)構(gòu)t i m e v a l包含下面的字段:
            struct timeval {
            int tv_sec; /* seconds */
            int tv_usec; /* microseconds */
            } ;
            把t v _ s e c設(shè)置成需要等待的時(shí)間秒數(shù),t v _ u s e c設(shè)置成需要等待的微秒數(shù)。一秒中包括1 000
            0 0 0 μ s 。下面的程序等待2 . 5 s:
            #include <sys/time.h>
            #include <sys/types.h>
            #include <unistd.h>
            #define STDIN 0 /* file descriptor for standard input */
            m a i n ( )
            {
            struct timeval tv;
            fd_set readfds;
            t v.tv_sec = 2;
            t v.tv_usec = 500000;
            F D _ Z E R O ( & r e a d f d s ) ;
            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");
            摘自:
            Linux系統(tǒng)高級(jí)編程-china pub
            posted on 2009-11-15 21:40 chatler 閱讀(733) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Socket
            <2009年11月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久国产香蕉视频| 国产精品久久波多野结衣| 亚洲精品无码久久不卡| 欧美亚洲国产精品久久| 久久人做人爽一区二区三区| 亚洲愉拍99热成人精品热久久| 日韩精品久久久久久免费| 色综合久久88色综合天天| 欧美亚洲日本久久精品| 性色欲网站人妻丰满中文久久不卡| 久久99精品久久久久久久不卡| 一级做a爰片久久毛片16| 亚洲欧洲精品成人久久曰影片| 欧美va久久久噜噜噜久久| 中文字幕成人精品久久不卡| 久久久黄色大片| 97超级碰碰碰碰久久久久| 久久经典免费视频| 伊人久久大香线焦综合四虎| 国产亚洲精品久久久久秋霞| 国产精品亚洲综合专区片高清久久久 | 人妻少妇久久中文字幕| 精品久久久久久无码中文野结衣 | 91精品日韩人妻无码久久不卡 | 久久99国产一区二区三区| 思思久久99热只有频精品66| 亚洲国产成人久久精品动漫| 亚洲人成伊人成综合网久久久| 精品99久久aaa一级毛片| 久久久噜噜噜www成人网| 色综合合久久天天给综看| 久久91综合国产91久久精品| 久久精品免费一区二区| 久久最新免费视频| 国产一区二区精品久久凹凸| 韩国免费A级毛片久久| 狠狠色噜噜色狠狠狠综合久久 | 久久久久亚洲AV无码去区首| 久久99精品国产一区二区三区 | www.久久热| 无码国产69精品久久久久网站|