Socket 與 TLI

  • 應用程式介面 (Application Program Interface, API)

  • Transport Layer Interface (TLI)

    • System Interface

    • Unix System V

  • Socket

    • Socket Library 

    • BSD Socket

      • Berkeley Sockets 程式設計
        「Socket」界面是源自柏克萊加州大學(UC Berkeley)為 BSD UNIX 4.1 版所添加的網路通訊系統呼叫;由於它的成功,日後更成為所有 UNIX 的標準設施。 這套 API,常特稱為 Berkeley Sockets
        爾後,Microsoft 為了要替 Windows 提供一套 TCP/IP API, 便根據 Berkeley Sockets 界面加以改造延伸,定義出 Windows Sockets,常簡稱為 WinSock
        Sun Microsystems 公司在為 Java 制定較低階的網路通訊 API 時, 也參照一些 Berkeley Sockets 的精神,再加以物件導向化。
        因為 Berkeley Sockets 出現較早、用戶較多、程式設計資源較豐沛,又為各派 TCP/IP API 的鼻祖,因此,瞭解 Berkeley Sockets,實為掌握各派 TCP/IP API 之無上捷徑。
 如果你對 Berkeley Sockets、Windows Sockets、Java 等各種 TCP/IP 網路程式設計 API 的綜合比較有興趣的話,可以參考 葉秉哲 在 1997/09 的 RUN!PC 雜誌發表的WWW 紮根系列(四):網路應用程式界面 一文。
  •  
    •  
      • Berkeley Sockets 概論
        Socket 的表面
        「Berkeley Sockets」是一組專供網路通訊的系統呼叫(程式庫)。
        當 process 想要進行網路通訊之前,要先向系統取得一個合法的 socket。各種 Berkeley Sockets 的系統呼叫,也都以這個 socket 為第一個參數。
        和 UNIX 的檔案系統 file descriptor(檔案代碼)類似,socket descriptor 也是以一個 int 來表示。

        Socket 的內部
        事 實上,在 UNIX 裡,socket descriptor 和 file descriptor 根本就是配置在同一個 descriptor table 裡頭的!所以,許多針對檔案做的 UNIX 系統呼叫,也能作用於 socket 上;很有 UNIX 的「大一統」精神。
        每個 socket 背後,都伴有一個連帶的資料結構,用來紀錄網路通訊的狀態。主要的狀態計有:
        欄位例子
        協定家族
        (protocol family)
        PF_INET(代表 TCP/IP 系列)。
        網路服務
        (service)
        SOCK_STREAM(代表 TCP)、SOCK_DGRAM(代表 UDP)、SOCK_RAW(代表 IP)。
        本地位址 140.113.23.33
        遠端位址 140.113.23.3
        本地通訊埠
        (port)
        5678
        遠端通訊埠 23

        




何謂 Socket ?

  • IP Address + TCP Port

  • 定義:

     Socket 就是一個網路上的通訊端點,使用者或應用程式只要連結到 Socket 便可以和網路上任何一個通訊端點連線,Socket 之間的通訊就如同作業系統內程序 (process) 之間通訊一樣。

 兩個程序之間的通信方法。Socket 是一種識別碼,應用程式可用此唯一識別通信端點。使用者建立 Socket 位址與 Socket 的關聯性後,便可建立通信協定位址與 Socket 的關聯性。

 Socket可譯為「插座」或者為「傳收口」,它是BSD 4.2版所提供的一種特殊型態的檔案。它的作用
  頗類似於管線(pipe),提供了處理程序之間通訊 (interprocess communication)的能力,並且
  支援了網路的功能。使用者可以用 "socket"系統呼叫(system call)來建立它,傳回值是一個 
  「檔案描述詞」(file descriptor)。

  「插座」有許多不同的種類,其目的是用來支援不同的「通訊語義」(communication semantic)
  如:資料可靠的傳遞、訊息先後次序的保持等;同時,「插座」比「管線」強的一點是,「管線」需
  要一個共同的父處理程序(parent process)來設立一個處理程序間的通訊管道,但是「插座」卻可 
  以由兩個完全不相關的處理程序來設立,甚至這兩個處理程序還可以存在於不同的機器上。為了支援
  「插座」系統核心(kernel)的結構需包含以下三個主要的部份:

  插座層(socketlayer)
  通訊協定層(protocal layer)
 
 設備層(device layer)
Socket 

語法
     #include <sys/types.h>;
     #include <sys/socket.h>;

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

函數描述
     創造一個通訊端點並傳回descriptor(似乎是類似檔案描述詞那種吧)

參數描述
      domain指定通訊位置 選擇使用的協定 其家族被定義在<sys/socket.h>;
        目前已知格式如下:

           PF_LOCAL           主機內部協定, 之前叫做 PF_UNIX,
           PF_UNIX               主機內部協定, deprecated, 使用 PF_LOCAL,
           PF_INET               Internet version 4 protocols, 
           PF_PUP                PUP protocols, like BSP,
           PF_APPLETALK   AppleTalk protocols,
           PF_ROUTE           Internal Routing protocol,
           PF_LINK                Link layer interface, 
           PF_IPX                  Novell Internet Packet eXchange protocol,
           PF_RTIP               Help Identify RTIP packets,
           PF_PIP                  Help Identify PIP packets,
           PF_ISDN               Integrated Services Digital Network, 
           PF_KEY                Internal key-management function,
           PF_INET6             Internet version 6 protocols,
           PF_NATM             Native ATM access,
           PF_ATM                ATM, 
           PF_NETGRAPH   Netgraph sockets

      socket也指定type 目前定義如下: 

           SOCK_STREAM     Stream socket,
           SOCK_DGRAM      Datagram socket,
           SOCK_RAW        Raw-protocol interface,
           SOCK_RDM        Reliably-delivered packet,
           SOCK_SEQPACKET  Sequenced packet stream 

      SOCK_STREAM
提供循序,可信賴,基於位元串流的雙方連線 Out-of-band的資料傳輸機制,可能被支援(亦即TCP連線)
使用IPv4網路協定;SOCK_STREAM=提供雙向連續且可信賴的資料流,即TCP. 
SOCK_STREAM 是全雙工字元串流 類似管線(pipe) 在傳送或接收任何資料前 stream socket 必須處於連線
狀態,可使用connect(2)建立連線,一旦連線建立,資料可以透過read(2)和write(2)或某些send(2)和recv(2) 
的變形來傳輸資料 (某些protocol家族,例如Internet家族,支援隱性連線的概念. 藉由sendto(2)建立連線建
立連線的封包上帶有資料),當session完成時 close(2)可能要執行 Out-of-band 資料,可能依據send(2)和
recv(2)裡描述的方式執行.

    用SOCK_STREAM的通訊協定確保資料沒有遺漏或重複,若資料不能在合理時間內傳送 calls會傳回-1,
並指定全域變數errno為ETIMEDOUT,若資料每幾分鍾強迫傳輸而無其他活動 過一段閒置時間後,發出
SIGPIPE訊號 如果不處理訊號的話就離開
            
      SOCK_DGRAM 
支援datagrams(無連線 不可依賴的固定,typically small,長度訊息)(亦即UDP連線).
SOCK_DGRAM 和SOCK_RAW 允許datagrams經由send(2)的傳送資料,通常由recvform(2)接收(傳回下個資 
料封包和其return address).

    fcntl(2)可以用來指定當收到out-of-band時接收SIGURG訊號的處理群組,這可能允許non-blocking IO和IO事
件,經由SIGIO的非同步訊號通知socket操作經由options 控制,這些options被定義在<sys/socket.h>; 
setsockopt(2)和getsockopt(2)被用來設定以及取得選項.
    
      SOCK_SEQPACKET 
提供一個循序,可信賴,雙向為基礎的固定長資料傳輸路徑,使用者可能在每個系統呼叫時 要讀一整
          個封包(不過好像還沒實作:p).
SOCK_SEQPACKET 使用和 SOCK_STREAM 相同的系統呼叫,唯一的不同是 read(2)只會傳回要求資料 
的數量,其他到達的封包將被忽略.
    
      SOCK_RAW 提供存取內部網路協定和介面 SOCK_RAW只有超級使用者才能使用.
    
      SOCK_RDM 是計畫中 但還未實作 這裡就不描述了.

    protocol指定要使用的通訊協定 通常一種協定支援一特定type,但是也有可能存在很多protocol 在這種情況下,特定protocol必須用這種方式指定 
    protocol的數字和使用的通訊領域(domain)有關 請見protocol(5)

傳回值 
    -1代表錯誤發生 否則傳回指向這個socket的descriptor

ERRORS
     The socket() system call fails if:

     [EPROTONOSUPPORT]  The protocol type or the specified protocol is not 
                        supported within this domain.

     [EMFILE]           The per-process descriptor table is full.

     [ENFILE]           The system file table is full.

     [EACCES]           Permission to create a socket of the specified type 
                        and/or protocol is denied.

     [ENOBUFS]          Insufficient buffer space is available.  The socket
                        cannot be created until sufficient resources are
                        freed. 


            




Socket 基本功能

  • 開啟插座 (Create a socket)

    • socket() 呼叫程式

  • 連結通訊位址 (Bind an Address)

    • bind() 呼叫程式

        
  • 連結到對方插座 (Connect to another socket)

    • connect() 呼叫程式

  • 接受對方端點連線 (Accept a socket connection)

    • accept() 呼叫程式

  • 傳送資料 (Transfer data)

    • read() 和 write() 呼叫程式

    • send() 和 recv() 呼叫程式

    • sendto() 和 recvfrom() 呼叫程式 (電報傳輸)

    • sendmsg() 和 recvmsg() 呼叫程式 (快速傳輸)

  • 停止插座操作

    • cloas() 呼叫程式


虛擬電路模式

  • 連接導向運作模式

  • TCP 埠口連接

  • Client/Server  主從式架構

        

 

電報傳輸模式

  • 非連接導向連線

  • UDP 埠口連接

  • Client/Server 主從式架構


        


Berkeley Socket API 庫存函數

  • Create a socket

    • socket()

      • 產生新的 socket,以供後續的網路連線之用。一個處理在執行網路I/O之前所需要做的第一件事就是呼叫Socket系統呼叫,
         具此宣告通訊協定的型態(Internet TCP,Internet UDP,XNS SPP等)。
      •   #include <sys/socket.h>
          #include <sys/types.h>
          int socket(int family, int type, int protocol);
          family參數為下列其中之一,
                     AF_UNIX      Unix internal protocols
                     AF_INET      Internet protocols
                     AF_NS        Xerox NS protocols 
                     AF_IMPLINK   IMP link layer
            傳收口type為下列其中之一:
                     SOCK_STREAM           資料流傳收口
                     SOCK_DGRAM            單一封包傳收口
                     SOCK_RAW           原始傳收口
                     SOCK_SEQPACKET        循序封包傳收口
                     SOCK_RDM           可靠傳遞的訊息封口(尚未製作完成)

            家族與型態並非可以任意組合。下面列出可能的組合以及這些組合真正使用
            的通訊協定。
        AF_UNIX  AF_INET  AF_NS
        SOCK_STREAM      YES    TCP   SPP
        SOCK_DGRAM       YES    UDP   IDP
        SOCK_RAW                   IP     YES
        SOCK_SEQPACKET                      SPP

      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>

        int sd1, sd2; /* socket descriptors */

        sd1 = socket( PF_INET, SOCK_STREAM, 0 ); /* connection-oriented socket */
        sd2 = socket( PF_INET, SOCK_DGRAM, 0 ); /* connectionless socket */
    • socketpair()

  • Name a socket

    • bind()

      • 配置並繫結本端的位址及通訊埠。即 指定一個名稱給一個無名稱的傳收口
      • #include <sys/types.h>
        #include <sys/socket.h>

        int bind ( int sockfdstruct sockaddr *myaddrint addrlen );
        第二個參數是一個無協定相關的位址結構指標, 第三個參數則是此位址結構的長度
      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>

        int sd; /* socket descriptor */
        int ret; /* return status */
        int myaddrlen; /* length of myaddr */
        struct sockaddr_in myaddr; /* Internet address structure */

        ret = bind( sd, (struct sockaddr *) &myaddr, myaddrlen );

         struct sockaddr 是定義在 <sys/socket.h> 裡頭:

        struct sockaddr {
          u_short sa_family; /* address family: AF_INET for Inernet */
          char sa_data[14]; /* up to 14 bytes of direct address */
        };

         struct sockaddr_in 是定義在 <netinet/in.h> 裡頭:

        struct sockaddr_in { /* Socket address, internet style. */
          short sin_family; /* Should be AF_INET */
          u_short sin_port; /* Port number in network byte order */
          struct in_addr sin_addr; /* IP address in network byte order */
          char sin_zero[8]; /* unused */
        };

         底下這些定義在 <netinet/in.h> 的函數/巨集, 可用來轉換網路與本機的位元組順序。

        函數轉換方向資料型態
        ntohl()、ntohs() 網路 → 本機 long、short
        htonl()、htons() 本機 → 網路 long、short
        ntohs()、ntohl() 網路 → 本機 u_short、u_long
        htons()、htonl() 本機 → 網路 u_short、u_long
  • Connects to a socket

    • listen()

      • 置 TCP socket 於被動模式,等待遠端的連線請求。即連接導向的服務者使用此系統呼叫表示它已準備好與其他委託者連接。
      •  使用例:

        int sd; /* socket descriptor */
        int queuelen; /* the max number of connection requests in the waiting queue */
        int ret; /* return status */

        ret = listen( sd, queuelen );
    • accept()

      • 讓 TCP socket 接受遠端來的連線請求。在連接導向服務者執行listen系統呼叫之後,實際的連接則是在某委託者等待連接
        而由服務者執行accept系統呼叫所建立。

         為此連線產生一個新的 socket。
         用這個新的 socket 來進一步與對方收發資料。
         原先的 socket 就能繼續等待新的連線請求。

         #include<sys/types.h>
         #include<sys/socket.h>
         int accept(int sockfd,struct sockaddr *peer,int *addrlen);
         
         accept從佇列中取出第一個連接要求並建立一個與sockfd同性質的傳收口。 
         如果佇列中沒有連接時,呼叫accept的處理會被停止。
         peer與addrlen參數用來傳回服務者的位址。addrlen是一個結果值(value_result)
         的參數:呼叫者在系統呼叫之前設定此參數值,系統呼叫再將結果存至變數。
         此結果值參數通常是由呼叫者設定為某緩衝區的大小,而系統呼叫將此參數更改為
         實際填入緩衝區的個數。對於此系統呼叫而言,呼叫者將addrlen設為sockaddr結構
         的大小,而sockaddr結構的位址則用peer參數傳遞。在返回時,addrlen包含的數目
         是系統呼叫真正存放在peer參數的個數。對於Internet與XNS位址結構而言,因為
         它們的長度是固定的(16字元),我們所存放於addrlen參數的值(16)也就是系統
         呼叫在peer參數所存放的個數。使用Unix領域位址時,傳進與傳回的結構長度值
         就有可能不同。

         此系統呼叫傳回的值最多有三個:一個整數傳回值,此值可能是一個表示錯誤的 
         號碼或是一個新的傳收口描述值,委託者處理(對等)的位址,以及此位址結構的
         長度(addrlen)。

         accept會假設服務者是一個協同服務者自動建立一個新的傳收口描述值。
         典型的處理過程如下:

         int  sockfd, newsockfd;
         if ((sockfd=socket(...))<0)
            err_sys(*socket error*); 
         if (bind(sockfd, ... )<0)
            err_sys(*listen error*);
         if (listen(sockfd,5)<0)
            err_sys(*listen error*);
         for (  ;  ;  ){
             newsockfd=accept(sockfd,...);
             if (newsockfd=0)
               err_sys(*accept error*); 
             if (fork()==0) {
             close(sockfd);
             doit(newsockfd);
             exit(0);
                 }
         close(newsockfd);
         }

         當一個連接要求被接收而且被接受之後,此處理呼叫fork,子處理負責有關連接
         之後的服務工作,父處理則繼續等待下一個連接要。求由accept所傳回的新的 
         傳收口描述值對應至一個完整的聯結:
         {通訊協定,當地位址,當地處理,遠端位址,遠端處理}

         與newsockfd相關的這5個元素已在accept傳回時被填入。從另一方面來說,
         傳給accept的sockfd參數所對應的只有其中3個元素而已。遠端位址與遠端處理
         在此時都尚未明確,直到accept傳回時才確定。這使得原來的處理(父處理)
         不必再建立另一個傳收口描述值,可以使用sockfd去accept其它的連接。因為 
         大部份的連接導向服務者都是屬協同服務者而不是重複服務者,所以系統可以
         繼續執行並在accept自動建立一個新的傳收口。如果我們使用重複服務者,其
         過程將如下所示:

         int  sockfd, newsockfd;
         if ((sockfd=socket(...))<0)
            err_sys(*socket error*); 
         if (bind(sockfd, ... )<0)
            err_sys(*listen error*);
         if (listen(sockfd,5)<0)
            err_sys(*listen error*);

         for (  ;  ;  ){
             newsockfd=accept(sockfd,...);
             if (newsockfd=0)
               err_sys(*accept error*); 
             doit(newsockfd);
             close(newsockfd);
         }

         在此,服務者使用已連接的傳收口描述值newsockfd處理連接要求。它接著使用
         close結束連接並且使用原描述值sockfd等待其它連接,這時的遠端位址與遠端
         處理仍然是未定。

      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>

        int sd, nsd; /* socket descriptors */
        int clntaddrlen; /* length of clntaddr */

        struct sockaddr_in clntaddr; /* client endpoint address */

        nsd = accept( sd, (struct sockaddr *)&clntaddr,&clntaddrlen );
    • connect()
      • client 用來和遠端 server 建立連線之用。即委託者在socket系統呼叫使用connect系統呼叫與服務者之間建立一個連接。
      • #include <sys/types.h>
        #include <sys/socket.h>
        int connect ( int sockfd , struct sockaddr *servaddrint addrlen );
        sockfd是一個由socket系統呼叫所傳回的傳收口描述值 第二參數為傳收口位址結構的指標第三個參數為傳收口位址結構的長度
      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>

        int sd; /* socket descriptor */
        int ret; /* return status */
        int servaddrlen; /* length of servaddr */
        struct sockaddr_in servaddr; /* server endpoint address */

        ret = connect( sd, (struct sockaddr* ) &servaddr, servaddrlen );
  • Transfer data

    • send() / write()

      • 傳送/寫入網路連線彼端的資料。
      •  send系統呼叫和 write 類似,但還能夠用來處理特殊狀況:

        #include <sys/types.h>
        #include <sys/socket.h>

        int flags; /* for handling special messages */

        count = send( sd, buf, len, flags );

        其中,flags 的值:
         MSG_OOB:收發 out-of-band(緊急)資料。
         MSG_PEEK:只閱讀、而不取出收到的封包資料。
         MSG_DONTROUTE:不予以 routing。

      •  一般的 UNIX write 系統呼叫,可用來對連線導向通訊進行資料寫入:

        int handle; /* socket descriptor */
        int count; /* number of bytes transmitted */
        char buffer[100]; /* transmission buffer */
        int len; /* max number of bytes transmitted */

        /*
          count = write(handle , buffer , len );
         handle :這是一個已開起的process代號。
         buffer :指一個緩衝區。
         len    :表示呼叫一次write動作,應寫入多少數量的字元。
         count  :表示系統實際所寫入的字元數。
        */

        count = write( handle, buffer, len ); /* send len bytes from buffer[] */

    • sendto()

      • 寫入至 UDP 連線的資料。
      •  sendto系統呼叫可用來發送非連線導向的網路資料。
      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>

        int sd; /* socket descriptor */
        int count; /* number of bytes transmitted */
        char buf[100]; /* transmission buffer */
        int len; /* max number of bytes transmitted */

        int flags ; /* for handling special messages */
        struct sockaddr_in to, from; /* endpoint addresses */
        int addrlen; /* length of address structure */
        count = sendto( sd, buf, len, flags, ( struct sockaddr *) &to, addrlen );

    • sendmsg()

    • recv() / read()

      • 接收/讀取網路連線彼端的資料。
      •  recv 系統呼叫和 read 類似,但還能夠用來處理特殊狀況:

        #include <sys/types.h>
        #include <sys/socket.h>

        int flags; /* for handling special messages */

        count = recv( sd, buf, len, flags );

        其中,flags 的值:
         MSG_OOB:收發 out-of-band(緊急)資料。
         MSG_PEEK:只閱讀、而不取出收到的封包資料。
         MSG_DONTROUTE:不予以 routing。

      •  一般的 UNIX read系統呼叫,可用來對連線導向通訊進行資料讀取:在一個已開啟的process中,讀取一定數量的字元,然後將這些所讀取的字元放入某個預存的緩衝區內。

        int handle; /* socket descriptor */
        int count; /* number of bytes transmitted */
        char buffer[100]; /* transmission buffer */ 
        int len; /* max number of bytes transmitted */

        /*
          count = read(handle , buffer , len );
         handle :這是一個已開起的process代號。
         buffer :指一個緩衝區。
         len    :表示呼叫一次read動作,應讀取多少數量的字元。
         count  :表示系統實際所讀取的字元數。 
        */
        count = read( handlebuffer , len );  /* receive at most len bytes to buffer[] */ 

    • recvfrom()

      • 讀取自 UDP 連線的資料。
      •  recvfrom 系統呼叫可用來收發非連線導向的網路資料。
      •  使用例:

        #include <sys/types.h>
        #include <sys/socket.h>

        int sd; /* socket descriptor */
        int count; /* number of bytes transmitted */
        char buf[100]; /* transmission buffer */
        int len; /* max number of bytes transmitted */

        int flags ; /* for handling special messages */
        struct sockaddr_in to, from; /* endpoint addresses */
        int addrlen; /* length of address structure */
        count = recvfrom( sd, buf, len, flags, (struct sockaddr*) &from, &addrlen );

    • recvmsg()

  send,sendto,recv與recvfrom系統呼叫
 這些系統呼叫與標準的read與write系統呼叫類似,但是需要額外的參數。

 #include<sys/types.h>
 #include<sys/socket.h>

 int send(int sockfd, char *buff, int nbytes, int flags);
 int sendto(int sockfd, char *buff, int nbytes, int flags,struct sockaddr *to ,int addrlen); 
 int recv(int sockfd, char *buff, int nbytes, int flags);
 int recvfrom(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to ,int addrlen);

  這四個系統呼叫的前三個參數,sockfd,buff 與 nbytes 與 read  write 的三個參數值類似。

  flags參數值可能為0或是下面三個值之一:

 MSG-OOB    傳送或接數緊急資料
 MSG_PEEK   拾起一個進來的訊息(recv或recvfrom) 
 MSD_DONTROUTE 經由路徑選擇(send或sendto)
 MSG_PEEK      旗幟讓呼叫者查看資料是否可以讀取,不需
                 在recv或recvfrom 返回後
 才讓系統將資
                 料刪除。
在sendto的to 參數宣告資料傳送
                 的目的地。因為此位址
與協定相關,所以它
                 的長度必須由
addrlen所宣告。revfrom系
                 統呼叫將傳送此
資料的位址填入from。此
                 位址的長度經由addrlen
傳給呼叫者。注意
                 到sendto的 
最後一個參數是一個整數,而
                 recvfrom的最後一個參數是一個
整數指標
                
(一個結果值的參數)。 

   4個系統呼叫都傳回讀取或寫入的資料長度當做此函數值。典型的
    recvfrom使用型式是在非連接型式協定時,其傳回值表示單一封
    包的長度。

  • Shutdown a socket

    • shutdown()

      • 關閉 socket 連線。
      •  shutdown 可用來「半關閉」socket 連線:

        int shutdown( int sd, int direction );

        其中,direction 的值:
         0:停止輸入。
         1:停止輸出。
         2:和 close 效果一樣。

    • close()

      • 關閉 socket 連線。正常的Unix close系統呼叫也用來關閉一個傳收口。

        int close(int fd);
        如果所要關閉的傳收口對應至一個保證可靠到達的協定(例如TCP或SPP),
        系統必須確定留在核心內尚未傳送的資料或回覆必須送出。系統通常立即從close
        返回,但是核心仍然嘗試將放在佇列的資料全部送出。
      •  使用一般的 UNIX 系統呼叫 close 來關閉 socket:

        int close( int sd );
  • manages socket

    • getsockname()

    • getpeername()

    • getsockopt()

  • 其他函數
    •  inet_addr:把數字型式的 IP 位址轉換成網路系統的二進位表示法。

       gethostbyname:把文字型式的 domain name 轉換成二進位型式的 hostent 資料結構。

       getservbyname 把網路協定及服務轉換成二進位型式的 servent 資料結構。

       <netdb.h> 包含的相關資料結構:

      struct hostent{
        char *h_name;       /* official host name */
        char **h_aliases;    /* other aliases */
        int h_addrtype;      /* address type */
        int h_length;          /* address length */
        char **h_addr_list; /* list of IP addresses */
      #define h_addr h_addr_list[0]
      };

      struct servent {
        char *s_name;     /* official service name */
        char **s_aliases;  /* alias list */
        int s_port;            /* port # in network byte order */
        char *s_proto;      /* protocol to use */
      };

       使用例:

      #include <sys/types.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <arpa/inet.h>
      #include <netdb.h>

      char *dotip="140.113.23.33";
      char *domainip="cis.nctu.edu.tw";
      unsigned long ip;
      struct hostent *hptr;
      struct servent *sptr;

      if ( ip = inet_addr(dotip) ){
          /* IP address is now in ip */
      } else {
          /* error in doted decimal */
      }

      if ( hptr = gethostbyname( domainip ) ) {
          /* IP address is now in hptr->h_addr */
      } else {
          /* error in domain name */
      }

      if ( sptr = getservbyname( "smtp", "tcp" ) ) {
          /* port number is now in sptr->s_port */
      } else {
          /* error in service name or protocol name */
      }


Berkeley Sockets 程式 範例

  • Server
    •  有用的程式庫:

      passiveTCP 建立被動的 TCP socket 連線。
      /* passiveTCP.c - passiveTCP */

      int passivesock(const char *service, const char *transport,
      int qlen);

      /*------------------------------------------------------------------------


      * passiveTCP - create a passive socket for use in a TCP server
      *------------------------------------------------------------------------
      */
      int
      passiveTCP(const char *service, int qlen)
      /*
      * Arguments:


      * service - service associated with the desired port
      * qlen - maximum server request queue length
      */
      {
      return passivesock(service, "tcp", qlen);
      }

      passiveUDP 建立被動的 UCP socket 連線。
      /* passiveUDP.c - passiveUDP */

      int passivesock(const char *service, const char *transport,
      int qlen);

      /*------------------------------------------------------------------------


      * passiveUDP - create a passive socket for use in a UDP server
      *------------------------------------------------------------------------
      */
      int
      passiveUDP(const char *service)
      /*
      * Arguments:


      * service - service associated with the desired port
      */
      {
      return passivesock(service, "udp", 0);
      }

      passivesock 供 passiveUDP 和 passiveTCP 使用。
      /* passivesock.c - passivesock */

      #include <sys/types.h>
      #include <sys/socket.h>

      #include <netinet/in.h>



      #include <stdlib.h>
      #include <string.h>
      #include <netdb.h>

      extern int errno;

      int errexit(const char *format, ...);

      u_short portbase = 0; /* port base, for non-root servers */



      /*------------------------------------------------------------------------
      * passivesock - allocate & bind a server socket using TCP or UDP
      *------------------------------------------------------------------------


      */
      int
      passivesock(const char *service, const char *transport, int qlen)
      /*
      * Arguments:
      * service - service associated with the desired port
      * transport - transport protocol to use ("tcp" or "udp")


      * qlen - maximum server request queue length
      */
      {
      struct servent *pse; /* pointer to service information entry */
      struct protoent *ppe; /* pointer to protocol information entry*/
      struct sockaddr_in sin; /* an Internet endpoint address */


      int s, type; /* socket descriptor and socket type */

      memset(&sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = INADDR_ANY;

      /* Map service name to port number */

      if ( pse = getservbyname(service, transport) )

      sin.sin_port = htons(ntohs((u_short)pse->s_port)
      + portbase);
      else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
      errexit("can't get \"%s\" service entry\n", service);



      /* Map protocol name to protocol number */
      if ( (ppe = getprotobyname(transport)) == 0)
      errexit("can't get \"%s\" protocol entry\n", transport);

      /* Use protocol to choose a socket type */


      if (strcmp(transport, "udp") == 0)
      type = SOCK_DGRAM;
      else
      type = SOCK_STREAM;

      /* Allocate a socket */
      s = socket(PF_INET, type, ppe->p_proto);
      if (s < 0)
      errexit("can't create socket: %s\n", strerror(errno));



      /* Bind the socket */
      if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
      errexit("can't bind to %s port: %s\n", service,
      strerror(errno));
      if (type == SOCK_STREAM && listen(s, qlen) < 0)


      errexit("can't listen on %s port: %s\n", service,
      strerror(errno));
      return s;
      }

       Iterative connection-oriented server 和 iterative connectionless server 例子。

 Iterative connection-oriented server

/* TCPechod.c - main, TCPechod */

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define QLEN 5 /* maximum connection queue length */
#define BUFSIZE 4096

extern int errno;

void reaper(int);

int TCPechod(int fd);
int errexit(const char *format, ...);
int passiveTCP(const char *service, int qlen);

/*------------------------------------------------------------------------

* main - Concurrent TCP server for ECHO service

*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *service = "echo"; /* service name or port number */

struct sockaddr_in fsin; /* the address of a client */

int alen; /* length of client's address */
int msock; /* master server socket */
int ssock; /* slave server socket */

switch (argc) {

case 1:
break;
case 2:
service = argv[1];

break;
default:
errexit("usage: TCPechod [port]\n");
}

msock = passiveTCP(service, QLEN);

(void) signal(SIGCHLD, reaper);


while (1) {
alen = sizeof(fsin);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);

if (ssock < 0) {
if (errno == EINTR)
continue;
errexit("accept: %s\n", strerror(errno));

}
switch (fork()) {
case 0: /* child */
(void) close(msock);
exit(TCPechod(ssock));

default: /* parent */
(void) close(ssock);
break;
case -1:
errexit("fork: %s\n", strerror(errno));

}
}
}

/*------------------------------------------------------------------------

* TCPechod - echo data until end of file
*------------------------------------------------------------------------

*/
int
TCPechod(int fd)
{
char buf[BUFSIZ];
int cc;

while (cc = read(fd, buf, sizeof buf)) {

if (cc < 0)
errexit("echo read: %s\n", strerror(errno));
if (write(fd, buf, cc) < 0)

errexit("echo write: %s\n", strerror(errno));
}
return 0;
}

/*------------------------------------------------------------------------

* reaper - clean up zombie children
*------------------------------------------------------------------------

*/
/*ARGSUSED*/
void
reaper(int sig)
{
int status;

while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)

/* empty */;
}

 iterative connectionless server


/* UDPechod.c
- main */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>

extern int errno;

int passiveUDP(const char *service);


int errexit(const char *format, ...);

#define LINELEN 128
/*------------------------------------------------------------------------
* main - (Iterative and Concurrent) UDP server for ECHO service

*------------------------------------------------------------------------

*/
int
main(int argc, char *argv[])
{
struct sockaddr_in fsin; /* the from address of a client */
char *service = "echo"; /* service name or port number */

char buf[LINELEN]; /* "input" buffer */

int nchars; /* message length */
int sock; /* server socket */
int alen; /* from-address length */

switch (argc) {
case 1:

break;
case 2:
service = argv[1];
break;

default:
errexit("usage: UDPechod [port]\n");
}

sock = passiveUDP(service);

while (1) {
alen = sizeof(fsin);

if (recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&fsin, &alen) < 0)

errexit("recvfrom: %s\n", strerror(errno));
nchars = strlen(buf);
(void) sendto(sock, buf, nchars, 0,

(struct sockaddr *)&fsin, sizeof(fsin));
}
}

 Concurrent connectionless server 例子

    同 Iterative connection-oriented server 範例

 Multi-Program concurrent connection-oriented server 例子。

/* MasterTCPechod.c - main, reaper */

#include <sys/types.h>

#include <sys/signal.h>

#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <netinet/in.h>


#include <unistd.h
>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define QLEN 5 /* maximum connection queue length */
#define BUFSIZE 4096

extern int errno;


void reaper(int);

int errexit(const char *format, ...);
int passiveTCP(const char *service, int qlen);

/*------------------------------------------------------------------------
* main - Master concurrent TCP server for ECHO service
*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *service = "echo"; /* service name or port number */
struct sockaddr_in fsin; /* the address of a client */


int alen; /* length of client's address */
int msock; /* master server socket */
int ssock; /* slave server socket */

switch (argc) {
case 1:
break;
case 2:
service = argv[1];


break;
default:
errexit("usage: TCPechod [port]\n");
}

msock = passiveTCP(service, QLEN);

(void) signal(SIGCHLD, reaper);

while (1) {
alen = sizeof(fsin);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);


if (ssock < 0) {
if (errno == EINTR)
continue;
errexit("accept: %s\n", strerror(errno));
}
switch (fork()) {
case 0: /* child */
(void) close(msock);
dup2(ssock, 0);


dup2(ssock, 1);
dup2(ssock, 2);
exit(exec(SlaveTCPechod));
default: /* parent */
(void) close(ssock);
break;
case -1:
errexit("fork: %s\n", strerror(errno));

}

}
}



/*------------------------------------------------------------------------
* reaper - clean up zombie children
*------------------------------------------------------------------------
*/
/*ARGSUSED*/
void
reaper(int sig)
{
int status;

while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
/* empty */;
}
 Slave server
  •  
    • /* SlaveTCPechod.c - main */

      #include <unistd.h>
      #include <
      stdlib.h>

      #include <stdio.h>
      #include <string.h>

      #define BUFSIZE 4096

      extern int errno;

      int errexit(const char *format, ...);

      /*------------------------------------------------------------------------


      * main - Slave concurrent TCP server for ECHO service
      - echo data until end of file
      *------------------------------------------------------------------------
      */
      int
      main()
      {
      char buf[BUFSIZ];


      int cc;

      while (cc = read(0, buf, sizeof buf)) {
      if (cc < 0)
      errexit("echo read: %s\n", strerror(errno));
      if (write(0, buf, cc) < 0)
      errexit("echo write: %s\n", strerror(errno));


      }
      return 0;
      }

       Multi-Process concurrent connection-oriented server 例子

/* TCPechod.c - main, TCPechod, reaper */

#include <sys/types.h>

#include <sys/signal.h>

#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <netinet/in.h>


#include <unistd.h
>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define QLEN 5 /* maximum connection queue length */
#define BUFSIZE 4096

extern int errno;


void reaper(int);

int TCPechod(int fd);
int errexit(const char *format, ...);
int passiveTCP(const char *service, int qlen);

/*------------------------------------------------------------------------
* main - Multi-Process Concurrent TCP server for ECHO service
*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *service = "echo"; /* service name or port number */

struct sockaddr_in fsin; /* the address of a client */

int alen; /* length of client's address */
int msock; /* master server socket */
int ssock; /* slave server socket */

switch (argc) {

case 1:
break;
case 2:
service = argv[1];

break;
default:
errexit("usage: TCPechod [port]\n");
}

msock = passiveTCP(service, QLEN);

(void) signal(SIGCHLD, reaper);


while (1) {
alen = sizeof(fsin);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);

if (ssock < 0) {
if (errno == EINTR)
continue;
errexit("accept: %s\n", strerror(errno));

}
switch (fork()) {
case 0: /* child */
(void) close(msock);
exit(TCPechod(ssock));

default: /* parent */
(void) close(ssock);
break;
case -1:
errexit("fork: %s\n", strerror(errno));

}
}
}

/*------------------------------------------------------------------------

* TCPechod - echo data until end of file
*------------------------------------------------------------------------

*/
int
TCPechod(int fd)
{
char buf[BUFSIZ];
int cc;

while (cc = read(fd, buf, sizeof buf)) {

if (cc < 0)
errexit("echo read: %s\n", strerror(errno));
if (write(fd, buf, cc) < 0)

errexit("echo write: %s\n", strerror(errno));
}
return 0;
}

/*------------------------------------------------------------------------

* reaper - clean up zombie children
*------------------------------------------------------------------------

*/
/*ARGSUSED*/
void
reaper(int sig)
{
int status;

while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)

/* empty */;
}
/* TCPmechod.c - main, echo */

#include <sys/types.h>


#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>

#define QLEN 5 /* maximum connection queue length */


#define BUFSIZE 4096

extern int errno;
int errexit(const char *format, ...);
int passiveTCP(const char *service, int qlen);
int echo(int fd);

/*------------------------------------------------------------------------


* main - Single Process Concurrent TCP server for ECHO service
*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *service = "echo"; /* service name or port number */


struct sockaddr_in fsin; /* the from address of a client */
int msock; /* master server socket */
fd_set rfds; /* read file descriptor set */
fd_set afds; /* active file descriptor set */
int alen; /* from-address length */


int fd, nfds;

switch (argc) {
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage: TCPmechod [port]\n");
}

msock = passiveTCP(service, QLEN);



nfds = getdtablesize();
FD_ZERO(&afds);
FD_SET(msock, &afds);

while (1) {
memcpy(&rfds, &afds, sizeof(rfds));

if (select(nfds, &rfds, (fd_set *)0, (fd_set *)0,


(struct timeval *)0) < 0)
errexit("select: %s\n", strerror(errno));
if (FD_ISSET(msock, &rfds)) {
int ssock;

alen = sizeof(fsin);
ssock = accept(msock, (struct sockaddr *)&fsin,


&alen);
if (ssock < 0)
errexit("accept: %s\n",
strerror(errno));
FD_SET(ssock, &afds);
}
for (fd=0; fd<nfds; ++fd)
if (fd != msock && FD_ISSET(fd, &rfds))


if (echo(fd) == 0) {
(void) close(fd);
FD_CLR(fd, &afds);
}
}
}

/*------------------------------------------------------------------------
* echo - echo one buffer of data, returning byte count


*------------------------------------------------------------------------
*/
int
echo(int fd)
{
char buf[BUFSIZ];
int cc;

cc = read(fd, buf, sizeof buf);
if (cc < 0)
errexit("echo read: %s\n", strerror(errno));


if (cc && write(fd, buf, cc) < 0)
errexit("echo write: %s\n", strerror(errno));
return cc;
}
  • Client
    •  有用的程式庫:

      connectTCP 建立 TCP socket 連線。
      /* connectTCP.c - connectTCP */


      int connectsock(const char *host, const char *service,
      const char *transport);


      /*------------------------------------------------------------------------
      * connectTCP - connect to a specified TCP service on a specified host


      *------------------------------------------------------------------------


      */

      int
      connectTCP(const char *host, const char *service )


      /*
      * Arguments:


      * host - name of host to which connection is desired
      * service - service name associated with the desired port


      * or port # in ASCII string, eg. "23" */


      {

      return connectsock( host, service, "tcp");


      }
      connectUDP 建立 UDP socket 連線。
      /* connectUDP.c - connectUDP */
      int connectsock(const char *host, const char *service, Const char *transport);
      /*------------------------------------------------------------------------


      * connectUDP - connect to a specified UDP service on a specified host
      *------------------------------------------------------------------------
      */
      int
      connectUDP(const char *host, const char *service )


      /*
      * Arguments:
      * host - name of host to which connection is desired
      * service - service name associated with the desired port
      * or port # in ASCII, eg. "23"
      */
      {

      return connectsock(host, service, "udp");

      }
      connectsock 供 connectTCP 和 connectUDP 使用。
      /* connectsock.c - connectsock */
      #include <sys/types.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <arpa/inet.h>


      #include <netdb.h>
      #include <string.h>
      #include <stdlib.h>
      #ifndef INADDR_NONE
      #define INADDR_NONE 0xffffffff
      #endif /* INADDR_NONE */
      extern int errno;

      int errexit(const char *format, ...);



      /*------------------------------------------------------------------------
      * connectsock - allocate & connect a socket using TCP or UDP
      *------------------------------------------------------------------------


      */
      int
      connectsock(const char *host, const char *service, const char *transport )
      /*
      * Arguments:
      * host - name of host to which connection is desired
      * service - service associated with the desired port


      * transport - name of transport protocol to use ("tcp" or "udp")
      */
      {
      struct hostent *phe; /* pointer to host information entry */
      struct servent *pse; /* pointer to service information entry */


      struct protoent *ppe; /* pointer to protocol information entry*/
      struct sockaddr_in sin; /* an Internet endpoint address */
      int s, type; /* socket descriptor and socket type */


      memset(&sin, 0, sizeof(sin));


      sin.sin_family = AF_INET;

      /* Map service name to port number */
      if ( pse = getservbyname(service, transport) )
      sin.sin_port = pse->s_port;
      else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )


      errexit("can't get \"%s\" service entry\n", service);

      /* Map host name to IP address, allowing for dotted decimal */
      if ( phe = gethostbyname(host) )
      memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);


      else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )
      errexit("can't get \"%s\" host entry\n", host);

      /* Map transport protocol name to protocol number */
      if ( (ppe = getprotobyname(transport)) == 0)


      errexit("can't get \"%s\" protocol entry\n", transport);

      /* Use protocol to choose a socket type */
      if (strcmp(transport, "udp") == 0)
      type = SOCK_DGRAM;

      else

      type = SOCK_STREAM;

      /* Allocate a socket */
      s = socket(PF_INET, type, ppe->p_proto);
      if (s < 0)
      errexit("can't create socket: %s\n", strerror(errno));

      /* Connect the socket */


      if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
      errexit("can't connect to %s.%s: %s\n", host, service,
      strerror(errno));
      return s;
      }
      errexit 讓 connectsock 顯示錯誤訊息並結束程式之用。
      /* errexit.c - errexit */

      #include <stdarg.h>
      #include <stdio.h>
      #include <stdlib.h>

      /*------------------------------------------------------------------------


      * errexit - print an error message and exit
      *------------------------------------------------------------------------
      */
      /*VARARGS1*/
      int
      errexit(const char *format, ...)
      {
      va_list args;



      va_start(args, format);
      vfprintf(stderr, format, args);
      va_end(args);
      exit(1);
      }

       TCP client 例子

 
/* 
TCPecho.c - main, TCPecho */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

extern int errno;

int TCPecho(const char *host, const char *service);


int errexit(const char *format, ...);
int connectTCP(const char *host, const char *service);

#define LINELEN 128

/*------------------------------------------------------------------------
* main - TCP client for ECHO service


*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "echo"; /* default service name */



switch (argc) {
case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: TCPecho [host [port]]\n");


exit(1);
}
TCPecho(host, service);
exit(0);
}

/*------------------------------------------------------------------------
* TCPecho - send input to ECHO service on specified host and print reply


*------------------------------------------------------------------------
*/
int
TCPecho(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
int s, n; /* socket descriptor, read count*/


int outchars, inchars; /* characters sent and received */

s = connectTCP(host, service);

while (fgets(buf, sizeof(buf), stdin)) {
buf[LINELEN] = '\0'; /* insure line null-terminated */


outchars = strlen(buf);
(void) write(s, buf, outchars);

/* read it back */
for (inchars = 0; inchars < outchars; inchars+=n ) {
n = read(s, &buf[inchars], outchars - inchars);

if (n < 0)

errexit("socket read failed: %s\n",
strerror(errno));
}
fputs(buf, stdout);
}
}
 
/* UDPecho.c - main, UDPecho */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

extern int errno;

int UDPecho(const char *host, const char *service);


int errexit(const char *format, ...);
int connectUDP(const char *host, const char *service);

#define LINELEN 128

/*------------------------------------------------------------------------
* main - UDP client for ECHO service


*------------------------------------------------------------------------
*/
int
main(int argc, char *argv[])
{
char *host = "localhost";
char *service = "echo";

switch (argc) {


case 1:
host = "localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage: UDPecho [host [port]]\n");


exit(1);
}
UDPecho(host, service);
exit(0);
}

/*------------------------------------------------------------------------
* UDPecho - send input to ECHO service on specified host and print reply


*------------------------------------------------------------------------
*/
int
UDPecho(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
int s, nchars; /* socket descriptor, read count*/



s = connectUDP(host, service);

while (fgets(buf, sizeof(buf), stdin)) {
buf[LINELEN] = '\0'; /* insure null-terminated */
nchars = strlen(buf);
(void) write(s, buf, nchars);



if (read(s, buf, nchars) < 0)
errexit("socket read failed: %s\n",
strerror(errno));
fputs(buf, stdout);
}
}
 
/* TCPtecho.c - main, TCPtecho, reader, writer, mstime */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/socket.h>



#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

extern int errno;

int TCPtecho(fd_set *pafds, int nfds, int ccount, int hcount);

int reader(int fd, fd_set *pfdset);

int writer(int fd, fd_set *pfdset);
int errexit(const char *format, ...);
int connectTCP(const char *host, const char *service);
long mstime(u_long *);

#define BUFSIZE 4096 /* write buffer size */


#define CCOUNT 64*1024 /* default character count */

#define USAGE "usage: TCPtecho [ -c count ] host1 host2...\n"

char *hname[NOFILE]; /* fd to host name mapping */
int rc[NOFILE], wc[NOFILE]; /* read/write character counts */


char buf[BUFSIZE]; /* read/write data buffer */


/*------------------------------------------------------------------------
* main - concurrent TCP client for ECHO service timing
*------------------------------------------------------------------------


*/
int
main(int argc, char *argv[])
{
int ccount = CCOUNT;
int i, hcount, maxfd, fd;
int one = 1;
fd_set afds;

hcount = 0;
maxfd = -1;
for (i=1; i<argc; ++i) {
if (strcmp(argv[i], "-c") == 0) {


if (++i < argc && (ccount = atoi(argv[i])))
continue;
errexit(USAGE);
}
/* else, a host */

fd = connectTCP(argv[i], "echo");
if (ioctl(fd, FIONBIO, (char *)&one))


errexit("can't mark socket nonblocking: %s\n",
strerror(errno));
if (fd > maxfd)
maxfd = fd;
hname[fd] = argv[i];
++hcount;
FD_SET(fd, &afds);
}
TCPtecho(&afds, maxfd+1, ccount, hcount);


exit(0);
}

/*------------------------------------------------------------------------
* TCPtecho - time TCP ECHO requests to multiple servers
*------------------------------------------------------------------------


*/
int
TCPtecho(fd_set *pafds, int nfds, int ccount, int hcount)
{
fd_set rfds, wfds; /* read/write fd sets */
fd_set rcfds, wcfds; /* read/write fd sets (copy) */
int fd, i;

for (i=0; i<BUFSIZE; ++i) /* echo data */


buf[i] = 'D';
memcpy(&rcfds, pafds, sizeof(rcfds));
memcpy(&wcfds, pafds, sizeof(wcfds));
for (fd=0; fd<nfds; ++fd)
rc[fd] = wc[fd] = ccount;

(void) mstime((u_long *)0); /* set the epoch */



while (hcount) {
memcpy(&rfds, &rcfds, sizeof(rfds));
memcpy(&wfds, &wcfds, sizeof(wfds));

if (select(nfds, &rfds, &wfds, (fd_set *)0,
(struct timeval *)0) < 0)


errexit("select failed: %s\n", strerror(errno));
for (fd=0; fd<nfds; ++fd) {
if (FD_ISSET(fd, &rfds))
if (reader(fd, &rcfds) == 0)
hcount--;
if (FD_ISSET(fd, &wfds))


writer(fd, &wcfds);
}
}
}

/*------------------------------------------------------------------------
* reader - handle ECHO reads
*------------------------------------------------------------------------


*/
int
reader(int fd, fd_set *pfdset)
{
u_long now;
int cc;

cc = read(fd, buf, sizeof(buf));
if (cc < 0)
errexit("read: %s\n", strerror(errno));
if (cc == 0)

errexit("read: premature end of file\n");

rc[fd] -= cc;
if (rc[fd])
return 1;
(void) mstime(&now);
printf("%s: %d ms\n", hname[fd], now);
(void) close(fd);
FD_CLR(fd, pfdset);

return 0;
}

/*------------------------------------------------------------------------

* writer - handle ECHO writes
*------------------------------------------------------------------------

*/
int
writer(int fd, fd_set *pfdset)
{
int cc;

cc = write(fd, buf, MIN(sizeof(buf), wc[fd]));

if (cc < 0)
errexit("read: %s\n", strerror(errno));
wc[fd] -= cc;
if (wc[fd] == 0) {

(void) shutdown(fd, 1);
FD_CLR(fd, pfdset);
}
}

/*------------------------------------------------------------------------

* mstime - report the number of milliseconds since Jan 1, 1970

*------------------------------------------------------------------------
*/
long
mstime(u_long *pms)
{
static struct timeval epoch;

struct timeval now;

if (gettimeofday(&now, (struct timezone *)0))

errexit("gettimeofday: %s\n", strerror(errno));
if (!pms) {
epoch = now;
return 0;
}
*pms = (now.tv_sec
- epoch.tv_sec) * 1000;
*pms += (now.tv_usec - epoch.tv_usec + 500)/ 1000;

return *pms;
}


虛擬電路程式範例

 

        

 

  • 連接 TCP 埠口

  • Echo Server 與 Echo Client

  • Server 端程式編譯與執行 (163.15.2.30 linux-2)

    • # cc -o tcpsrv tcpsrv.c

    • # tcpsrv  &

  • Client 端程式編譯與執行 (163.15.2.62 linux-1)

    • # cc -o tcpcln tcpcln.c

    • # tcpcln  163.15.2.30  file_a

TCP 伺服端程式範例

  1. 開啟 Socket (呼叫 socket())

  2. 建立位址格式 (sockaddr_in)

  3. 定址 Socket (呼叫 bind())

  4. 進入聆聽狀態 (呼叫 listen())

  5. 等待連線要求 (呼叫 accept()),如有連線要求,再進入下一步驟。

  6. 讀取 Client 端傳送的資料 (呼叫 read())

  7. 顯示 Client 端傳送的資料

  8. 回傳資料給 Client 端 (呼叫 write())

  9. 回到步驟 5,等待下一個 Client 端要求

TCP 客戶端程式範例

  1. 檢查執行程式之參數

  2. 開啟 Socket (呼叫 socket())

  3. 建立位址格式 (sockaddr_in)

  4. 定址 Socket (呼叫 bind())

  5. 填入 Server 端位址

  6. 連線至 Server 端 (呼叫 connect())

  7. 開啟並讀取檔案

  8. 傳送訊息給 Server 端 (呼叫 write())

  9. 讀回並顯示訊息 (呼叫 read())

電報傳輸程式範例

  • UDP 傳輸埠口

  • Echo Server 與 Echo Client

  • Server 端程式編譯與執行 (163.15.2.30 linux-2)

    • # cc -o udpsrv udpsrv.c

    • # udpsrc  &

  • Client 端程式編譯與執行 (163.15.2.62 linux-1)

    • # cc -o udpcln udpcln.c

    • # udpcln 163.15.2.30 file_a

UDP 伺服端程式範例

  1. 開啟 Socket (呼叫 socket())

  2. 建立位址格式 (sockaddr_in)

  3. 定址 Socket (呼叫 bind())

  4. 進入等待接收 (呼叫 recvfrom()),如有收到資料,再進入下一步驟。

  5.   顯示 Client 端資料

  6. 建立對方傳輸埠口位址 (呼叫 ient_ntoa())

  7. 回傳資料給 Client 端 (呼叫 sendto())

  8. 回到步驟 4,等待下一個 Client 端要求

UDP 客戶端程式範例

  1. 檢查執行程式之參數

  2. 開啟 Socket (呼叫 socket())

  3. 建立位址格式 (sockaddr_in)

  4. 定址 Socket (呼叫 bind())

  5. 填入 Server 端位址  (UDP/IP 位址)

  6. 開啟並讀取檔案

  7. 傳送訊息給 Server 端 (呼叫 sendto())

  8. 讀回並顯示訊息 (呼叫 recvfrom())

Socket 多工輸入/輸出

  • select() 功能呼叫


Socket 多工連線

  • fork() 功能呼叫

  • 多工連線功能圖




Socket 多工範例 - xinetd

  • xinetd 多工原理

    • 多工輸入/輸出

    • 多工連線

  • xinetd 多工運作程序



arrow
arrow
    全站熱搜

    Bluelove1968 發表在 痞客邦 留言(1) 人氣()