Socket 編程原理簡介

來源:互聯網
上載者:User

 

 

1 如何進行Socket編程  

    進程通訊的概念最初來源於單機系統,由於每個進程都在各自的位址範圍內運行,為了保證兩個相互連信的進程之間既不互相干擾,又協調一致的工作,作業系統為進程通訊提供了相應設施,如UNIX BSD中的管道(pipe),有名管道(named pipe)和非強制中斷訊號(singal),UNIX system V的訊息(message)、共用儲存區(shared memory)和訊號量(semaphore)等,但都局限於用在本機進程之間通訊。網間進程通訊要解決的是不同主機處理序間的通訊問題(可把同機進程通訊看作其中的特例)。為此,首先要解決的是網間進程標識問題。同一主機上,不同進程可以用進程號(pid)唯一標識。但在網路環境下,各主機獨立分配的進程號不能唯一標識該進程。例如主機A賦予某進程號5,在B主機也可以存在5號進程,因此5號進程這句話就沒有意義了。其次,作業系統支援的網路通訊協定眾多,不同協議的工作方式不同,地址格式也不同。因此,網間進程通訊還要解決多重協議的識別問題。為瞭解決上述問題,TCP/IP協議引入了下列幾個概念。  

連接埠  
    網路中可以被命名和定址的通訊連接埠是作業系統可分配的一種資源。按照OSI七層協議的描述,傳輸層與網路層最大的區別是傳輸層提供進程通訊能力。從這個意義上講,網路通訊的最終地址就不僅是主機地址了,還包括可以描述進程的某種標識符。為此TCP/IP協議提出了協議連接埠的概念,用於標識通訊的進程。  
    連接埠是一種抽象的軟體結構,包括一些資料結構和I/O緩衝區。應用程式即進程通過系統調用與某連接埠建立串連(binding)後,傳輸層傳給該連接埠的資料都被相應的進程所接收,相應進程發給傳輸層的資料都從該連接埠輸出。在TCP/IP協議的實現中,連接埠操作類似於一般的I/O操作,進程擷取一個連接埠,相當於擷取本地唯一的I/O檔案,可以用一般的讀寫原語訪問。  
    類似於檔案描述符,每個連接埠都擁有一個叫連接埠號碼的整數描述符,以區別不同連接埠。由於TCP/IP傳輸層的兩個協議TCP和UDP是兩個完全獨立的軟體模組,因此各自的連接埠號碼也相互獨立。如TCP有一個255號連接埠,UDP也可以有一個255號連接埠,兩者並不衝突。  
    連接埠號碼的分配是一個重要問題,有兩種基本分配方式:第一種叫全域分配這是一種集中分配方式,由一個公認的中央機構根據使用者需要盡行統一分配,並將結果公佈於眾,第二種是本地分配,又稱動態串連,即進程需要訪問傳輸層服務時,向本地作業系統提出申請,作業系統返回本地唯一的連接埠號碼,進程再通過合適的系統調用,將自己和該連接埠串連起來(綁定)。TCP/IP連接埠號碼的分配綜合了兩種方式。TCP/IP將連接埠號碼分為兩部分,少量的作為保留連接埠,以全域方式分配給服務進程。因此,每一個標準伺服器都擁有一個全域公認的連接埠叫周知口,即使在不同的機器上,其連接埠號碼也相同。剩餘的為自由連接埠,以本地方式進行分配。TCP和UDP規定,小於256的連接埠才能作為保留連接埠。  

地址  
    網路通訊中的兩個進程分別在兩個不同的機器上。在互連網路中,兩台機器可以位於不同的網路,這些網路通過網際互連裝置(網關,橋接器,路由器)串連。因此需要三級定址。  
    1。某一主機與多個網路相連,必須指定一特定網路地址;  
    2。網路上美一台主機應有其唯一的地址;  
    3。美一主機上的每一進程應有在該主機上的唯一標識。  
    主機地址就是IP啦,不必多說。進程唯一識別碼是十六位整數連接埠號碼。  

網路位元組順序  
    不同的電腦存放多位元組值的順序不同,有的機器在起始地址存放低位位元組,有的則相反。為保證資料的正確性,在網路通訊協定中需指定網路位元組順序。TCP/IP協議使用16位整數和32位整數的高價先存格式,他們均含在協議的標頭檔中。  

串連  
    兩個進程間的通訊鏈路稱為串連。串連在內部表現為一些緩衝區和一組協議機制,在外部表格現出比無串連高的可靠性。  

半相關  

 

 

2 如何進行Socket編程

 
    綜上所述,網路中用一個三元組可以在全域中唯一標是一個進程:(協議,本機地址,本地連接埠號碼)這樣一個三元組,叫做一個半相關,他指定串連的每半部分。全相關  
    一個完整的網間進程通訊需要有兩個進程組成,並且只能使用同一種高層協議。也就是說TCP和UDP沒法通訊。因此一個完整的網間進程通訊需要一個五元組來標識:  
    (協議,本機地址,本地連接埠號碼,遠地地址,遠地連接埠號碼)這樣一個五元組叫做一個全相關。  

    在TCP/IP網路應用中,通訊的兩個進程相互作用的主要模式是客戶機/伺服器模式,即用戶端向伺服器發出請求,伺服器接收到請求後提供相應的服務客戶機/伺服器模式的建立基於以下兩點:首先,建立網路的起因是網路中軟、硬體資源、運算能力和資訊不均等,需要共用,從而造就擁有眾多資源的主機提供服務,資源較少的客戶請求服務這一非對等作用。其次,網間進程通訊完全是非同步,相互連信的進程間既不存在父子關係,又不共用記憶體緩衝區,因此需要一種機製為希望通訊的進程間建立一種聯絡,為二者的資料交換提供同步,這就是基於客戶機/伺服器模式的TCP/IP。  
    客戶機/伺服器模式在操作過程中採取的是主動請求方式:  
    首先伺服器方要啟動,並根據請求提供相應服務:  
    1。開啟一通訊通道並告知本地主機,它願意在某一公認地址連接埠上(周知口,如http為80)接受客戶請求。  
    2。等待客戶請求到達該連接埠。  
    3。接收到重複服務要求,處理該請求並發送應答訊號。接收並發服務要求,要啟用一新進程來處理這個客戶請求。新進程處理此客戶請求,並不需要對其他請求做出應答。服務完成後,關閉此新進程與客戶的通訊鏈路,並終止。  
    4。返回第二步,等待另外的客戶請求  
    5。關閉伺服器。  
    客戶方:  
    1。開啟一通訊通道,並串連到伺服器所在主機的特定連接埠。  
    2。向伺服器發出服務要求報文,等待並接收應答;繼續提出請求。  
    3。請求結束後關閉通訊通道並終止。  
    從上面的描述過程可知:  
    1。客戶與伺服器處理序的作用是非對稱的。因此編碼不同。  
    2。服務進程一般是先於客戶請求啟動的。只要系統運行,該進程一直存在,直到正常終止或者強迫終止。 

    在UNIX世界中,網路應用編程介面有兩類:BSD的通訊端SOCKET和SYSTEM V的TLI.由於Sun公司採用了支援TCP/IP的BSD系統,TCP/IP的應用有了更大發展其網路應用編程介面Socket在網路編程中已成為標準。並且也早已經進入了MS的世界。  
    TCP/IP的Socket提供下列三種類型的通訊端  
    1。流式通訊端(SOCKET_STREAM)  
    提供了一個連線導向,可靠的Data Transmission Service,資料無差錯,無重複的發送且按發送順序接收。內設流量控制,避免資料流超限;資料被看作是位元組流,無長度限制。FTP協議即採用流式通訊端。  
    2。資料報式通訊端(SOCKET_DGRAM)  
    提供了一個無串連服務。資料包以獨立包形式被發送,不提供無錯保證,資料可能丟失或重複,並且接收順序無序。網路檔案系統NFS使用資料報式通訊端。  
    3。原始式通訊端(SOCKET_RAW)  
    該介面允許對較低層次協議,如IP、ICMP直接存取。常用於檢驗新的協議實現或訪問現有服務中配置的新裝置。  

基本通訊端調用  

    建立通訊端--socket();  
    綁定本機連接埠--bind();  
    建立串連--connect(),accept();  
    偵聽連接埠--listen();  
    資料轉送--send(),recv();  
    輸入/輸出多工--select();  
    關閉通訊端--closesocket();  
      
    不論何種語言,和socket打交道都是這一組調用只是在格式上有一點點差別。我只使用過c和perl,再加上這裡又不讓出現與perl無關的東西,那下面就主要討論PERL的socket編程啦:  

建立建通訊端:  

socket(SOC_VARIABLE,DOMAIN_FLAG,connectType,num)  #相應的C語言調用為sockid=socket(af,type,protocol)  

   
   
   

3 如何進行Socket編程(轉)
 
參數含義如下:  
SOC_VARIABLE是用於建立套接的控制代碼,相當於c裡面的sockid號;DOMAIN_FLAG叫域標記,在C裡相當於af--address family,地址族。地址族和域是一個概念,其實就是平常所說的域。UNIX支援的域類型有  
AF-UNIX;      UNIX內部地址  
   AF-INET;      TCP/IP地址  
   AF-NS;        Xerox NS地址  
   AF-APPLETALK; Apple的Appletalk地址  
而dos/windows支援的域地址族只有AF-INET.所以大部分的socket編程都只用到它。  
connectType(c裡的type)就是前面所說的三種socket類型。num相當於c裡面的protocol那大家一看就明白了這是協議號,用來指定SOCKET請求所希望的協議,這個參數不一定起作用,當前兩個參數可以確定協議時可以取值為零。  
所以,一個完整的PERL的建立socket如下  
socket(THESCK,AF-INET,SOCKET_STREAM,getprotocolbyname('tcp'));  
#c語言: int sockid;  
#        sockid = socket(AF-INET,SOCKET_STREAM,0);  

第二步了:bind()--綁定到本地地址。  
    第一步socket()調用只是指定了相關五元組的協議元。其他四元需要別的調用來補充。socket的建立可以認為建立了一個名字空間(地址族),但他沒有被命名。bind()將通訊端地址與本機建立的通訊端控制代碼(c裡面的通訊端號)綁定,即將名字賦予通訊端(控制代碼),以指定本地半相關。按照標準的socket來說(在UNIX世界裡所謂編程的"標準介面"就和說"c的編程介面"沒啥區別),通訊端地址是一個描述socket地址的資料結構。其中描述TCP/IP協議地址(AF_INET)的結構為:  
struct sockaddr_in{  
   short sin_family; //AF_INET  
   u_short sin_port; //16位連接埠號碼,網路位元組順序  
   struct in_addr sin_addr; //32位IP地址,網路位元組順序  
   char sin_zero[8]; //保留  
}  
其他的結構還有 sockaddr_ns,sockaddr_un等,是用在其他協議地址的。基本上我們用不到。於是一個標準綁定為:  

bind(SOCKET sockid,struct * localaddr_name,int addrlen);  
//sockid是一個未命名通訊端的socket號  
//localaddr_name是用來命名sockid的指向sockaddr_in結構的指標  
//addrlen是localaddr_name的位元組長度  

使用perl的bind()時先要調用inet_aton('localhost');或函數 INADDR_ANY 取得ip地址字串,然後調用  
$localaddr_port = sockaddr_in($port,inet_aton('localhost')); #$port是連接埠號碼  
或者是 $localaddr_port = sockaddr_in($port,INADDR_ANY); 獲得TCP/IP的地址,最後bind(SERVER,$localaddr_port);  

完成綁定!在這裡不用指定$localaddr_port的位元組長度,這就是perl的好處。  

    這兩個系統調用用於完成一個全相關的建立,其中connect()用於建立串連。accept()使伺服器等待來自某客戶進程的實際串連。其調用格式如下  
 connect(SOCKET sockid,struct sockaddr* destaddr,int addrlen);  
 //sockid是欲建立串連的本地socket號  
 //destaddr是一個指向對方socket地址(信宿地址)結構的指標  
 //addrlen是對方socket地址長度  
   
perl語言的connect()調用格式為:  
connect(SOC_VARIABLE,NAME_VARIABLE)  
具體的調用過程如下  
$remoteaddr_port = sockaddr_in($port,inet_aton('abc.efg.com'));  
connect(CLIENT,$remoteaddr_port); #半相關三元組(協議,遠地地址,遠地連接埠號碼)。 
可以發現connect()和bind()調用如出一轍,只是SERVER換成CLIENT,local換成remote,沒錯,他們的道理是一樣的,作用是互補的。他們各自建立了伺服器和客戶機方面的半相關。這時就要accept()來摻和一下一個完整的網間進程通訊的全相關就可以建立啦!(其實標準connect()還可以用於不需連線的socket調用。但這個用法比較左道,常把人搞暈點,所以就不說了)  
標準accept()調用:  
SOCKET newsock = accept(SOCKET sockid,struct sockaddr* clientaddr,int  addlen)  
  //sockid ,伺服器本地的socket號  
  //clientaddr ,指向客戶socket結構的指標。它的初始值為空白  
  //addlen ,客戶socket結構的位元組長度,它的初始值為0  
  //newsock ,accept()的傳回值,為一個新的socket號,可用於伺服器  //處理並發請求。伺服器fork一個子伺服器處理序,利用此socket號回 
  //答accept()所接收的客戶請求  
可以看出,accept()是連線導向的伺服器端的調用。他還將用戶端的socket地址及其位元組長度放在clientaddr和addlen以便為其他不需連線的調用指定信宿地址。但是,那些比較靈活但沒有原則的無串連用法在perl裡是沒有的,perl的用法認為socket必須是連線導向的,請看perl裡的accept():  

  accept(NEW_SOC_VARIABLE,CURRENT_SOC_VARIABLE);  

    可以看到accept()從當前的socket控制代碼上串連出一個用戶端到新的socket控制代碼。而它的傳回值用戶端的地址(信宿地址)。其實串連一旦建立,服務方無須知道信宿地址,只要向套口裡灌輸位元流就行了。這樣做的好處是使協議對於應用程式更加透明,易於理解。  

在調用accept()之前還應該先調用listen(),listen()的作用是偵聽連接埠,接收串連。如果不調用listen()的話,accept()將無法從當前socket"接"回與用戶端的串連。標準listen():  

    listen(sockid,quelen);  
    //socket號,伺服器願意從它上面接收請求  
    //quelen,請求隊列長度,listen()以此限制排隊請求的個數  
      
perl的listen():  

    listen(SOC_VARIABLE,num);#和C語言的版本很像哦  
      
    SOC_VARIABLE是socket控制代碼,num是請求隊列長度.  
    到這裡為止,一個串連的五元組都齊了。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.