• <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系統網絡編程的內容,如套接口的概念與使用、網絡編程的結構等。
            1 什么是套接口
            簡單地說,套接口就是一種使用UNIX系統中的文件描述符和系統進程通信的一種方法。
            因為在UNIX系統中,所有的I/O操作都是通過讀寫文件描述符而產生的。文件描述符就是
            一個和打開的文件相關連的整數。但文件可以是一個網絡連接、一個FIFO、一個管道、一個
            終端、一個真正存儲在磁盤上的文件或者UNIX系統中的任何其他的東西。所以,如果你希望
            通過Internet和其他的程序進行通信,你只有通過文件描述符。
            使用系統調用socket(),你可以得到socket()描述符。然后你可以使用send() 和recv()調用而
            與其他的程序通信。你也可以使用一般的文件操作來調用read() 和write()而與其他的程序進行
            通信,但send() 和recv()調用可以提供一種更好的數據通信的控制手段。下面我們討論Internet
            套接口的使用方法。
            2 兩種類型的Internet套接口
            有兩種最常用的Internet 套接口,“數據流套接口”和“數據報套接口”,以后我們用
            “SOCK_STREAM” 和“SOCK_DGRAM”分別代表上面兩種套接口。數據報套接口有時也
            叫做“無連接的套接口”。
            數據流套接口是可靠的雙向連接的通信數據流。如果你在套接口中以“ 1, 2”的順序放入
            兩個數據,它們在另一端也會以“1, 2”的順序到達。它們也可以被認為是無錯誤的傳輸。
            經常使用的telnet應用程序就是使用數據流套接口的一個例子。使用HTTP的WWW瀏覽器
            也使用數據流套接口來讀取網頁。事實上,如果你使用telnet 登錄到一個WWW站點的8 0端口,
            然后鍵入“GET 網頁名”,你將可以得到這個HTML頁。數據流套接口使用TCP得到這種高質
            量的數據傳輸。數據報套接口使用UDP,所以數據報的順序是沒有保障的。數據報是按一種應
            答的方式進行數據傳輸的。
            3 網絡協議分層
            由于網絡中的協議是分層的,所以上層的協議是依賴于下一層所提供的服務的。也就是說,
            你可以在不同的物理網絡中使用同樣的套接口程序,因為下層的協議對你來說是透明的。
            UNIX系統中的網絡協議是這樣分層的:
            • 應用層( telnet、ftp等)。
            • 主機到主機傳輸層( TCP、UDP )。
            • Internet層( IP和路由)。
            • 網絡訪問層(網絡、數據鏈路和物理層)。
            4 數據結構
            下面我們要討論使用套接口編寫程序可能要用到的數據結構。
            首先是套接口描述符。一個套接口描述符只是一個整型的數值: i n t。
            第一個數據結構是struct sockaddr,這個數據結構中保存著套接口的地址信息。
            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包括一
            個目的地址和一個端口地址。
            你也可以使用另一個數據結構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 */
            } ;
            這個數據結構使得使用其中的各個元素更為方便。要注意的是sin_zero應該使用bzero() 或
            者memset()而設置為全0。另外,一個指向sockaddr_in數據結構的指針可以投射到一個指向數
            據結構sockaddr的指針,反之亦然。
            5 IP地址和如何使用IP地址
            有一系列的程序可以使你處理I P地址。
            首先,你可以使用inet_addr()程序把諸如“132.241.5.10”形式的IP地址轉化為無符號的整
            型數。
            ina.sin_addr.s_addr = inet_addr("132.241.5.10");
            如果出錯,inet_addr()程序將返回- 1。
            也可以調用inet_ntoa()把地址轉換成數字和句點的形式:
            printf ("%s", inet_ntoa (ina.sin_addr));
            這將會打印出I P地址。它返回的是一個指向字符串的指針。
            5.1 socket()
            我們使用系統調用socket()來獲得文件描述符:
            #include <sys/types.h>
            #include <sys/socket.h>
            int socket(int domain, int type, int protocol);
            第一個參數domain設置為“AF_INET”。第二個參數是套接口的類型:SOCK_STREAM 或
            SOCK_DGRAM。第三個參數設置為0。
            系統調用socket()只返回一個套接口描述符,如果出錯,則返回- 1。
            5.2 bind()
            一旦你有了一個套接口以后,下一步就是把套接口綁定到本地計算機的某一個端口上。但
            如果你只想使用connect()則無此必要。
            下面是系統調用bind()的使用方法:
            #include <sys/types.h>
            #include <sys/socket.h>
            int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
            第一個參數sockfd 是由socket()調用返回的套接口文件描述符。第二個參數my_addr 是指向
            數據結構sockaddr的指針。數據結構sockaddr中包括了關于你的地址、端口和I P地址的信息。
            第三個參數addrlen可以設置成sizeof(struct sockaddr)。
            下面是一個例子:
            #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));
            如果出錯,bind() 也返回- 1。
            如果你使用connect()系統調用,那么你不必知道你使用的端口號。當你調用connect()時,
            它檢查套接口是否已經綁定,如果沒有,它將會分配一個空閑的端口。
            5.3 connect()
            系統調用connect()的用法如下:
            #include <sys/types.h>
            #include <sys/socket.h>
            int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
            第一個參數還是套接口文件描述符,它是由系統調用socket()返回的。第二個參數是
            serv_addr是指向數據結構sockaddr的指針,其中包括目的端口和I P地址。第三個參數可以使用
            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; /* 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));
            同樣,如果出錯, c o n n e c t ( )將會返回- 1。
            5.4 listen()
            如果你希望不連接到遠程的主機,也就是說你希望等待一個進入的連接請求,然后再處理
            它們。這樣,你通過首先調用l i s t e n ( ),然后再調用a c c e p t ( )來實現。
            系統調用l i s t e n ( )的形式如下:
            int listen(int sockfd, int backlog);
            第一個參數是系統調用s o c k e t ( )返回的套接口文件描述符。第二個參數是進入隊列中允許
            的連接的個數。進入的連接請求在使用系統調用a c c e p t ( )應答之前要在進入隊列中等待。這個
            值是隊列中最多可以擁有的請求的個數。大多數系統的缺省設置為2 0。你可以設置為5或者1 0。
            當出錯時,l i s t e n ( )將會返回- 1值。
            當然,在使用系統調用l i s t e n ( )之前,我們需要調用b i n d ( )綁定到需要的端口,否則系統內
            核將會讓我們監聽一個隨機的端口。所以,如果你希望監聽一個端口,下面是應該使用的系統
            調用的順序:
            s o c k e t ( ) ;
            b i n d ( ) ;
            l i s t e n ( ) ;
            /* accept() goes here */
            5.5 accept()
            系統調用a c c e p t ( )比較起來有點復雜。在遠程的主機可能試圖使用c o n n e c t ( )連接你使用
            l i s t e n ( )正在監聽的端口。但此連接將會在隊列中等待,直到使用a c c e p t ( )處理它。調用a c c e p t ( )
            之后,將會返回一個全新的套接口文件描述符來處理這個單個的連接。這樣,對于同一個連接
            來說,你就有了兩個文件描述符。原先的一個文件描述符正在監聽你指定的端口,新的文件描
            述符可以用來調用s e n d ( )和r e c v ( )。
            調用的例子如下:
            #include <sys/socket.h>
            int accept(int sockfd, void *addr, int *addrlen);
            第一個參數是正在監聽端口的套接口文件描述符。第二個參數a d d r是指向本地的數據結構
            s o c k a d d r _ i n的指針。調用c o n n e c t ( )中的信息將存儲在這里。通過它你可以了解哪個主機在哪個
            端口呼叫你。第三個參數同樣可以使用sizeof(struct sockaddr_in)來獲得。
            如果出錯,a c c e p t ( )也將返回- 1。下面是一個簡單的例子:
            #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);
            下面,我們將可以使用新創建的套接口文件描述符n e w _ f d來調用s e n d ( )和r e c v ( )。
            5.6 send() 和recv()
            系統調用s e n d ( )的用法如下:
            int send(int sockfd, const void *msg, int len, int flags);
            第一個參數是你希望給發送數據的套接口文件描述符。它可以是你通過s o c k e t ( )系統調用
            返回的,也可以是通過a c c e p t ( )系統調用得到的。第二個參數是指向你希望發送的數據的指針。
            第三個參數是數據的字節長度。第四個參數標志設置為0。
            下面是一個簡單的例子:
            char *msg = "Beej was here!";
            int len, bytes_sent;
            len = strlen(msg);
            bytes_sent = send(sockfd, msg, len, 0);
            系統調用s e n d ( )返回實際發送的字節數,這可能比你實際想要發送的字節數少。如果返回
            的字節數比要發送的字節數少,你在以后必須發送剩下的數據。當s e n d ( )出錯時,將返回- 1。
            系統調用r e c v ( )的使用方法和s e n d ( )類似:
            int recv(int sockfd, void *buf, int len, unsigned int flags);
            第一個參數是要讀取的套接口文件描述符。第二個參數是保存讀入信息的地址。第三個參
            數是緩沖區的最大長度。第四個參數設置為0。
            系統調用r e c v ( )返回實際讀取到緩沖區的字節數,如果出錯則返回- 1。
            這樣使用上面的系統調用,你可以通過數據流套接口來發送和接受信息。
            5.7 sendto() 和recvfrom()
            因為數據報套接口并不連接到遠程的主機上,所以在發送數據包之前,我們必須首先給出
            目的地址,請看:
            int sendto(int sockfd, const void *msg, int len, unsigned int flags,
            const struct sockaddr *to, int tolen);
            除了兩個參數以外,其他的參數和系統調用s e n d ( )時相同。參數t o是指向包含目的I P地址
            和端口號的數據結構s o c k a d d r的指針。參數t o l e n可以設置為sizeof(struct sockaddr)。
            系統調用s e n d t o ( )返回實際發送的字節數,如果出錯則返回- 1。
            系統調用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);
            參數f r o m是指向本地計算機中包含源I P地址和端口號的數據結構s o c k a d d r的指針。參數
            f r o m l e n設置為sizeof(struct sockaddr)。
            系統調用r e c v f r o m ( )返回接收到的字節數,如果出錯則返回- 1。
            5.8 close() 和shutdown()
            你可以使用c l o s e ( )調用關閉連接的套接口文件描述符:
            c l o s e ( s o c k f d ) ;
            這樣就不能再對此套接口做任何的讀寫操作了。
            使用系統調用s h u t d o w n ( ),可有更多的控制權。它允許你在某一個方向切斷通信,或者切
            斷雙方的通信:
            int shutdown(int sockfd, int how);
            第一個參數是你希望切斷通信的套接口文件描述符。第二個參數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()
            這個系統的調用十分簡單。它將告訴你是誰在連接的另一端:
            #include <sys/socket.h>
            int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
            第一個參數是連接的數據流套接口文件描述符。第二個參數是指向包含另一端的信息的數
            據結構s o c k a d d r的指針。第三個參數可以設置為sizeof(struct sockaddr)。
            如果出錯,系統調用將返回- 1。
            一旦你獲得了它們的地址,你可以使用inet_ntoa() 或者g e t h o s t b y a d d r ( )來得到更多的信息。
            5.10 gethostname()
            系統調用g e t h o s t n a m e ( )比系統調用g e t p e e r n a m e ( )還簡單。它返回程序正在運行的計算機的
            名字。系統調用g e t h o s t b y n a m e ( )可以使用這個名字來決定你的機器的I P地址。
            下面是一個例子:
            #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”,即域名服務器。它可以把域名翻譯成相應的I P地址。
            你可以使用此I P地址調用b i n d ( )、c o n n e c t ( )、s e n d t o ( )或者用于其他的地方。
            系統調用g e t h o s t b y n a m e ( )可以完成這個函數:
            #include <netdb.h>
            struct hostent *gethostbyname(const char *name);
            它返回一個指向數據結構h o s t e n t的指針,數據結構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 —主機的正式名稱。
            h_aliases —主機的別名。
            h _ a d d r t y p e—將要返回的地址的類型,一般是A F _ I N E T。
            h _ l e n g t h—地址的字節長度。
            h _ a d d r _ l i s t—主機的網絡地址。
            h_addr —h _ a d d r _ l i s t中的第一個地址。
            系統調用g e t h o s t b y n a m e ( )返回一個指向填充好的數據結構h o s t e n t的指針。當發生錯誤時,
            則返回一個N U L L指針。下面是一個實際例子:
            #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 ( )時,你不能使用p e r r o r ( )來打印錯誤信息。你應該使用的是系統調用
            h e r r o r ( )。
            7 客戶機/服務器模式
            在網絡上大部分的通信都是在客戶機/服務器模式下進行的。例如t e l n e t。當你使用t e l n e t連
            接到遠程主機的端口2 3時,主機上的一個叫做t e l n e t d的程序就開始運行。它處理所有進入的
            t e l n e t連接,為你設置登錄提示符等。
            應當注意的是客戶機/服務器模式可以使用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。每當你使用f t p時,遠程計算機都在運
            行一個f t p d為你服務。
            一般情況下,一臺機器上只有一個服務器程序,它通過使用f o r k ( )來處理多個客戶端程序
            的請求。最基本的處理方法是:服務器等待連接,使用a c c e p t ( )接受連接,調用f o r k ( )生成一個
            子進程處理連接。
            8 簡單的數據流服務器程序
            此服務器程序所作的事情就是通過一個數據流連接發送字符串“ Hello, Wo r l d ! \ n”。你可以
            在一個窗口上運行此程序,然后在另一個窗口使用t e l n e t:
            $ telnet remotehostname 3490
            其中,r e m o t e h o s t n a m e是你運行的機器名。下面是此程序的代碼:
            #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 */
            }
            }
            你也可以使用下面的客戶機程序從服務器上得到字符串。
            9 簡單的數據流客戶機程序
            客戶機所做的是連接到你在命令行中指定的主機的3 4 9 0端口。它讀取服務器發送的字符
            串。
            下面是客戶機程序的代碼:
            #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;
            }
            如果你在運行服務器程序之前運行客戶機程序,則將會得到一個“ Connection refused”的
            信息。
            10 數據報套接口
            程序l i s t e n e r在機器中等待端口4 9 5 0到來的數據包。程序t a l k e r向指定的機器的4 9 5 0端口發
            送數據包。
            下面是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;
            }
            你可以在一臺機器上運行l i s t e n e r程序,在另一臺機器上運行t a l k e r程序,然后觀察它們之
            間的通信。
            21.11 阻塞
            當使用上面的listener程序時,此程序在等待直到一個數據包到來。這是因為它調用了
            recvform(),如果沒有數據,recvform ( )就一直阻塞,直到有數據到來。
            很多函數都有阻塞。系統調用accept ( )阻塞,所有的類似recv*()的函數也可以阻塞。它們
            之所以可以阻塞是因為系統內核允許它們阻塞。當你第一次創建一個套接口文件描述符時,系
            統內核將它設置為可以阻塞。如果你不希望套接口阻塞,你可以使用系統調用fcntl():
            #include <unistd.h>
            #include <fcntl.h>
            .
            sockfd = socket(AF_INET, SOCK_STREAM, 0);
            fcntl(sockfd, F_SETFL, O_NONBLOCK);
            .
            如果你設置為不阻塞,那么就得頻繁地詢問套接口以便檢查有無信息到來。如果你試圖讀
            取一個沒有阻塞的套接口,同時它又沒有數據,那么你將得到- 1。
            詢問套接口以檢查有無信息得到來可能會占用太多的C P U時間。另一個可以使用的方法是
            select()。
            select()用于同步I / O多路復用。這個系統調用十分有用。考慮一下下面的情況:你是一個
            服務器,你希望監聽進入的連接,同時還一直從已有的連接中讀取信息。
            也許你認為可以使用一個accept()調用和幾個recv()調用。但如果調用accept()阻塞了怎么
            辦?如果在這時你希望調用recv()接受數據呢?
            系統調用select()使得你可以同時監視幾個套接口。它可以告訴你哪一個套接口已經準備好
            了以供讀取,哪一個套接口已經可以寫入。
            下面是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);
            此函數監視幾個文件描述符,特別是r e a d f d s、w r i t e f d s和e x c e p t f d s。如果你希望檢查是否
            可以從標準輸入中和一些其他的套接口文件描述符s o c k f d中讀取數據,只需把文件描述符0和
            s o c k f d添加到r e a d f d s中。參數n u m f d s應該設置為最高的文件描述符的值加1。
            當s e l e c t ( )返回時,r e a d f d s將會被修改以便反映你選擇的那一個文件描述符已經準備好了以
            供讀取。你可以使用F D _ I S S E T ( )測試。
            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 是否在文件描述符集中。
            數據結構t i m e v a l包含下面的字段:
            struct timeval {
            int tv_sec; /* seconds */
            int tv_usec; /* microseconds */
            } ;
            把t v _ s e c設置成需要等待的時間秒數,t v _ u s e c設置成需要等待的微秒數。一秒中包括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系統高級編程-china pub
            posted on 2009-11-15 21:40 chatler 閱讀(728) 評論(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
            • 感覺這個博客還是不錯,雖然做的東西和我不大相關,覺得看看還是有好處的

            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

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            国产精品久久久久…| 国产女人aaa级久久久级| 国产日韩久久久精品影院首页| 亚洲人成精品久久久久 | 成人久久免费网站| 亚洲国产成人久久综合碰| 久久精品成人一区二区三区| 丁香久久婷婷国产午夜视频| 久久综合狠狠色综合伊人| 国产韩国精品一区二区三区久久| 人妻无码中文久久久久专区| 亚洲第一极品精品无码久久| 久久亚洲精品中文字幕| 国内精品久久久久伊人av| 99久久久精品免费观看国产| 精品一区二区久久久久久久网站| 99久久免费国产特黄| 国内精品久久久久久久久| 久久久久人妻精品一区三寸蜜桃| 久久精品国产精品亚洲| 久久久久国产精品麻豆AR影院| 欧美国产成人久久精品| 亚洲香蕉网久久综合影视| 亚洲国产欧美国产综合久久| 精品无码久久久久国产| 亚洲欧美日韩精品久久| 四虎国产精品成人免费久久| 日韩精品久久无码人妻中文字幕| 久久久久久伊人高潮影院| 欧美国产成人久久精品| 99久久精品国产麻豆| 精品久久久无码中文字幕| 亚洲欧美成人久久综合中文网 | 久久久久se色偷偷亚洲精品av| 欧洲精品久久久av无码电影| 99久久精品免费看国产免费| 久久精品中文字幕大胸| 欧美一区二区精品久久| 97精品伊人久久久大香线蕉| 国产精品免费久久久久电影网| 免费精品久久天干天干|