【RL-TCPnet網絡教程】第18章 BSD Sockets基礎知識

第18章      BSD Sockets基礎知識

本章節爲你們講解BSD Sockets,須要你們對BSD Sockets有個基礎的認識,方便後面章節Socket實戰操做。程序員

(本章的知識點主要整理自網絡)編程

18.1  初學者重要提示服務器

18.2  Socket基礎知識參考資料網絡

18.3  Socket基礎知識點數據結構

18.4  BSD Sockets簡介dom

18.5  BSD Sockets的API說明socket

18.6  總結編程語言

 

18.1  初學者重要提示

    初學者務必要對Socket的基礎知識點有個認識,不是特別理解沒有關係,隨着後面逐漸的實戰操做,會有比較全面的認識。函數

 

18.2  Socket基礎知識參考資料

首次搞Socket,須要對Socket的一些基礎知識有個瞭解。你們能夠從如下地址得到Socket基礎知識,下面是Socket參考資料:學習

下面是BSD Sockets參考資料:

對於初學者來講,學習上面六個參考資料就夠了。若是你們有網絡方面的書籍,好比《TCP/IP詳解》,也能夠直接看書籍。

 

18.3  Socket基礎知識點

(這裏的知識點整理自上面的參考資料地址)

教程這裏也對Socket的基礎知識作個介紹,方便你們快速上手操做。

18.3.1 網絡套接字(Network Socket)

在計算機科學中,網絡套接字,又譯網絡接口、網絡插槽,是電腦網絡中進程間數據流的端點。使用以網際協議IP爲通訊基礎的網絡套接字,稱爲網際套接字Internet Socket。由於網際協議的流行,現代絕大多數的網絡套接字,都是屬於網際套接字。

Socket是一種操做系統提供的進程間通訊機制。Socket最初被翻譯爲 「媒介(字)」。不久,ARPANET的Socket就被翻譯爲「套接字」,其理由是:

因爲每一個主機系統都有各自命名進程的方法,並且經常是不兼容的,所以,要在全網範圍內硬把進程名字統一塊兒來是不現實的。因此,每一個計算機網絡中都要引入一種起媒介做用的、全網一致的標準名字空間。這種標準名字,在ARPA網中稱做套接字,而在不少其餘計算機網中稱做信口。更確切地說,進程之間的鏈接是經過套接字或信口構成的。

在操做系統中,一般會爲應用程序提供一組應用程序接口,稱爲套接字接口(Socket API)。應用程序能夠經過套接字接口,來使用網絡套接字,以進行數據交換。最先的套接字接口來自於4.2 BSD,所以現代常見的套接字接口大多源自Berkeley套接字(Berkeley Sockets)標準。

------------------------------------------

網絡上的兩個程序經過一個雙向的通訊鏈接實現數據的交換,這個鏈接的一端稱爲一個Socket。創建網絡通訊鏈接至少要一對端口號(Socket)。Socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員作網絡開發所用的接口,這就是Socket編程接口。HTTP是轎車,提供了封裝或者顯示數據的具體形式,Socket是發動機,提供了網絡通訊的能力。

Socket的英文原義是「孔」或「插座」。做爲BSD UNIX的進程通訊機制,取後一種意思。一般也稱做"套接字",用於描述IP地址和端口,是一個通訊鏈的句柄,能夠用來實現不一樣虛擬機或不一樣計算機之間的通訊。在Internet上的主機通常運行了多個服務軟件,同時提供幾種服務。每種服務都打開一個Socket,並綁定到一個端口上,不一樣的端口對應於不一樣的服務。Socket正如其英文原意那樣,像一個多孔插座。一臺主機猶如佈滿各類插座的房間,每一個插座有一個編號,有的插座提供220伏交流電,有的提供110伏交流電,有的則提供有線電視節目。客戶軟件將插頭插到不一樣編號的插座,就能夠獲得不一樣的服務。

 

18.3.2 Socket形象解釋

Socket很是相似於電話插座。以一個國家級電話網爲例,電話的通話雙方至關於相互通訊的2個進程,區號是它的網絡地址;區內一個單位的交換機至關於一臺主機,主機分配給每一個用戶的局內號碼至關於Socket號。任何用戶在通話以前,首先要佔有一部電話機,至關於申請一個Socket;同時要知道對方的號碼,至關於對方有一個固定的Socket。而後向對方撥號呼叫,至關於發出鏈接請求(假如對方不在同一區內,還要撥對方區號,至關於給出網絡地址)。假如對方在場並空閒(至關於通訊的另外一主機開機且能夠接受鏈接請求),拿起電話話筒,雙方就能夠正式通話,至關於鏈接成功。雙方通話的過程,是一方向電話機發出信號和對方從電話機接收信號的過程,至關於向Socket發送數據和從Socket接收數據。通話結束後,一方掛起電話機至關於關閉Socket,撤消鏈接。

在電話系統中,通常用戶只能感覺到本地電話機和對方電話號碼的存在,創建通話的過程,話音傳輸的過程以及整個電話系統的技術細節對他都是透明的,這也與Socket機制很是類似。Socket利用網間通訊設施實現進程通訊,但它對通訊設施的細節絕不關心,只要通訊設施能提供足夠的通訊能力,它就知足了。

至此,咱們對Socket進行了直觀的描述。抽象出來,Socket實質上提供了進程通訊的端點。進程通訊以前,雙方首先必須各自建立一個端點,不然是沒有辦法創建聯繫並相互通訊的。正如打電話以前,雙方必須各自擁有一臺電話機同樣。

在網間內部,每個Socket用一個半相關描述(協議,本地地址,本地端口)。一個完整的Socket有一個本地惟一的Socket號,由操做系統分配。最重要的是,Socket是面向客戶/服務器模型而設計的,針對客戶和服務器程序提供不一樣的Socket系統調用。客戶隨機申請一個Socket(至關於一個想打電話的人能夠在任何一臺入網電話上撥號呼叫),系統爲之分配一個Socket號;服務器擁有全局公認的Socket,任何客戶均可以向它發出鏈接請求和信息請求(至關於一個被呼叫的電話擁有一個呼叫方知道的電話號碼)。

Socket利用客戶/服務器模式巧妙地解決了進程之間創建通訊鏈接的問題。服務器Socket半相關被全局所公認很是重要。你們不妨考慮一下,兩個徹底隨機的用戶進程之間如何創建通訊?假如通訊雙方沒有任何一方的Socket固定,就比如打電話的雙方彼此不知道對方的電話號碼,要通話是不可能的。

 

18.3.3 Sockets鏈接過程

根據鏈接啓動的方式以及本地套接字要鏈接的目標,套接字之間的鏈接過程能夠分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。

(1)    服務器監聽:是服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態。

(2)    客戶端請求:是指由客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。

(3)    鏈接確認:是指當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求,它就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,鏈接就創建好了。而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。

大致流程圖以下:

18.4 Windows Sockets簡介

Windows Sockets是Windows下獲得普遍應用的、開放的、支持多種協議的網絡編程接口。從1991年的1.0版,通過不斷完善並在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成爲Windows網絡編程的事實上的標準。

Windows Sockets規範以U.C.Berkeley大學BSD UNIX中流行的Socket接口爲範例定義了一套Microsoft Windows下網絡編程接口。它不只包含了人們所熟悉的BerkeleySocket風格的庫函數;也包含了一組針對Windows的擴展庫函數,以使程序員能充分地利用Windows消息驅動機制進行編程。WindowsSockets規範本意在於提供給應用程序開發者一套簡單的API,並讓各家網絡軟件供應商共同遵照。此外,在一個特定版本Windows的基礎上,WindowsSockets也定義了一個二進制接口(ABI),以此來保證應用WindowsSocketsAPI的應用程序可以在任何網絡軟件供應商的符合WindowsSockets協議的實現上工做。所以這份規範定義了應用程序開發者可以使用,而且網絡軟件供應商可以實現的一套庫函數調用和相關語義。遵照這套WindowsSockets規範的網絡軟件,咱們稱之爲WindowsSockets兼容,而WindowsSockets兼容實現的提供者,咱們稱之爲WindowsSockets提供者。一個網絡軟件供應商必須百分之百地實現WindowsSockets規範才能作到WindowsSockets兼容。任何可以與WindowsSockets兼容實現協同工做的應用程序就被認爲是具備WindowsSockets接口。咱們稱這種應用程序爲WindowsSockets應用程序。WindowsSockets規範定義並記錄瞭如何使用API與Internet協議族(IPS,一般咱們指的是TCP/IP)鏈接,尤爲要指出的是全部的WindowsSockets實現都支持流套接字接口和數據報套接字接口,應用程序調用WindowsSockets的API實現相互之間的通信。WindowsSockets又利用下層的網絡通信協議功能和操做系統調用實現實際的通信工做。

 

18.5 BSD Sockets簡介

Berkeley sockets,又稱BSD sockets,是一種應用程序接口,用於網際套接字和Unix域套接字(Unix domain sockets),包括了一個用C語言寫成的應用程序開發庫,主要用於實現進程間通信。BSD Sockets能夠在不少不一樣的輸入/輸出設備和驅動之上運行,儘管這有賴於操做系統的具體實現。接口實現用於TCP/IP協議,所以它是維持Internet的基本技術之一。它是由加利福尼亞的伯克利大學開發,最初用於Unix系統。 現在,全部的現代操做系統都有一些源於Berkeley套接字接口的實現,它已成爲鏈接Internet的標準接口。

BSD Sockets剛開始是4.2BSD Unix操做系統(於1983發佈)的一套應用程序接口。然而,因爲AT&T的專利保護着Unix,因此只有在1989年伯克利大學才能自由地發佈本身的操做系統和網絡庫。

Berkeley套接字應用程序接口造成了事實上的網絡套接字的標準精髓。大多數其餘的編程語言使用與這套用C語言寫成的應用程序接口相似的接口。這套應用程序接口也被用於Unix域套接字。

 

18.5.1 使用BSD Sockets的系統

因爲Berkeley套接字是第一個socket,大多數程序員很熟悉它們,因此大量系統把伯克利套接字做爲其主要的網絡API,好比下面四個:

  • Windows Sockets (Winsock) ,和Berkeley Sockets很類似,最初是爲了便於移植Unix程序。
  • Java Sockets
  • Python sockets
  • Perl sockets

 

18.5.2 BSD Sockets的頭文件

    Berkeley套接字接口的定義在幾個頭文件中。這些文件的名字和內容與具體的實現之間有些許的不一樣。 大致上包括:
<sys/socket.h>
    BSD套接字核心函數和數據結構。
    AF_INET、AF_INET6 地址集和它們相應的協議集PF_INET、PF_INET6。 普遍用於Internet,這些包括了IP地址和TCP、UDP端口號。
<netinet/in.h>
    AF_INET 和AF_INET6 地址家族和他們對應的協議家族 PF_INET 和 PF_INET6。在互聯網編程中普遍使用,包括IP地址以及TCP和UDP端口號。(有待查閱,跟socket.h的功能說明重複了
<sys/un.h>
    PF_UNIX/PF_LOCAL 地址集。用於運行在一臺計算機上程序間的本地通訊,不用於網絡通信。
<arpa/inet.h>
   處理數值型IP地址的函數。
<netdb.h>
    將協議名和主機名翻譯爲數值地址的函數,搜索本地數據以及DNS。

 

18.5.3 BSD Sockets的API函數

這個列表是BSD Sockets API庫提供的函數概要(這裏的介紹,有個瞭解便可,下一章節會專門講解RL-TCPnet提供的Socket API):

socket() 
    建立一個新的肯定類型的套接字,類型用一個整型數值標識(文件描述符),併爲它分配系統資源。
bind() 
    通常用於服務器端,將一個套接字與一個套接字地址結構相關聯,好比,一個指定的本地端口和IP地址。
listen() 
    用於服務器端,使一個綁定的TCP套接字進入監聽狀態。
connect() 
    用於客戶端,爲一個套接字分配一個自由的本地端口號。若是是TCP套接字的話,它會試圖得到一個新的TCP鏈接。
accept() 
    用於服務器端。它接收一個從TCP客戶端發出的鏈接請求並建立一個新的套接字,並與該鏈接相應的套接字地址相關聯。
send()和recv(),或者write()和read(),或者recvfrom()和sendto(), 
    用於套接字的數據收發。
close() 
    用於釋放套接字資源。若是是TCP,鏈接會被中斷。
gethostbyname()和gethostbyaddr()
    用於解析主機名和地址。
select() 
    用於修整有以下狀況的套接字列表:準備讀,準備寫或者有錯誤。
poll() 
    用於檢查套接字的狀態。套接字能夠被測試,看是否能夠寫入、讀取或是有錯誤。
getsockopt() 
    用於查詢指定的套接字中一個特定的套接字選項的當前值。
setsockopt() 
    用於爲指定的套接字設定一個特定的套接字選項。

 

18.5.4 BSD Sockets支持的協議

套接字API是Unix網絡的通用接口,容許使用各類網絡協議和地址。下面列出了BSD Sockets支持的協議,如今的 Linux 和 BSD 中通常都已經實現了。

PF_LOCAL, PF_UNIX, PF_FILE  Local to host (pipes and file-domain)

PF_INET                     IP protocol family

PF_AX25                     Amateur Radio AX.25

PF_IPX                      Novell Internet Protocol

PF_APPLETALK                Appletalk DDP

PF_NETROM                   Amateur radio NetROM

PF_BRIDGE                   Multiprotocol bridge

PF_ATMPVC                   ATM PVCs

PF_X25                      Reserved for X.25 project

PF_INET6                    IP version 6

PF_ROSE                     Amateur Radio X.25 PLP

PF_DECnet                   Reserved for DECnet project

PF_NETBEUI                  Reserved for 802.2LLC project

PF_SECURITY                 Security callback pseudo AF

PF_KEY                      PF_KEY key management API

PF_NETLINK, PF_ROUTE        routing API

PF_PACKET                   Packet family

PF_ASH                      Ash

PF_ECONET                   Acorn Econet

PF_ATMSVC                   ATM SVCs

PF_SNA                      Linux SNA Project

PF_IRDA                     IRDA sockets

PF_PPPOX                    PPPoX sockets

PF_WANPIPE                  Wanpipe API sockets

PF_BLUETOOTH                Bluetooth sockets

 

18.6 BSD Sockets的API說明

說明:這些知識點整理自wiki中文

這裏主要將幾個主要的BSD Sockets API作個介紹(這裏的介紹,有個瞭解便可,下一章節會專門講解RL-TCPnet提供的Socket API):

18.6.1 函數socket

函數原型:

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

函數描述:

socket() 爲通信建立一個端點,爲套接字返回一個文件描述符。socket() 有三個參數:

  • 第1個參數domain 爲建立的套接字指定協議集(或稱作地址族 address family)。 例如:

        AF_INET 表示IPv4網絡協議。

        AF_INET6 表示IPv6。

        AF_UNIX 表示本地套接字(使用一個文件)。

  • 第2個參數type(socket類型) 以下:

        SOCK_STREAM (可靠的面向流服務或流套接字)。

        SOCK_DGRAM (數據報文服務或者數據報文套接字)。

        SOCK_SEQPACKET (可靠的連續數據包服務)。

        SOCK_RAW (在網絡層之上自行指定運輸層協議頭,即原始套接字)。

  • 第3個參數protocol 指定實際使用的傳輸協議。 最多見的就是IPPROTO_TCP、IPPROTO_SCTP、IPPROTO_UDP、IPPROTO_DCCP。這些協議都在<netinet/in.h>中有詳細說明。 若是該項爲「0」的話,即根據選定的domain和type選擇使用缺省協議。
  • 返回值,若是發生錯誤,函數返回值爲-1。不然,函數會返回一個表明新分配的描述符的整數。

 

18.6.2 函數bind

函數原型: 

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

函數描述:

bind() 爲一個套接字分配地址。當使用socket()建立套接字後,只賦予其所使用的協議,並未分配地址。在接受其它主機的鏈接前,必須先調用bind()爲套接字分配一個地址。bind()有三個參數:

  • 第1個參數sockfd, 表示使用bind函數的套接字描述符。
  • 第2個參數my_addr, 指向sockaddr結構體(用於表示所分配地址)的指針變量。
  • 第3個參數addrlen, 用socklen_t字段指定了sockaddr結構的長度。
  • 返回值,若是發生錯誤,函數返回值爲-1,不然爲0。

 

18.6.3 函數listen()

函數原型: 

int listen(int sockfd, int backlog);

函數描述:

當socket和一個地址綁定以後,listen()函數會開始監聽可能的鏈接請求。然而,這隻能在有可靠數據流保證的時候使用,例如:數據類型(SOCK_STREAM, SOCK_SEQPACKET)。

listen()函數須要兩個參數:

  • 第1個參數sockfd, 表示socket的描述符。
  • 第2個參數backlog, 表示監聽隊列大小,當有一個鏈接請求到來,就會進入此監聽隊列;當一個鏈接請求被accept()接受,則從監聽隊列中移出;當隊列滿後,新的鏈接請求會返回錯誤。
  • 返回值,一旦鏈接被接受,返回0表示成功,錯誤返回-1。

 

18.6.4 函數accept()

函數原型: 

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

函數描述:

當應用程序監聽來自其餘主機的數據流鏈接時,經過事件(好比Unix select()系統調用)通知它。必須用 accept()函數初始化鏈接。Accept() 爲每一個鏈接創立新的套接字並從監聽隊列中移除這個鏈接。它使用以下參數:

  • 第1個參數sockfd,監聽的套接字描述符。
  • 第2個參數cliaddr,指向sockaddr 結構體的指針,客戶機地址信息。
  • 第3個參數addrlen,指向 socklen_t的指針,肯定客戶機地址結構體的大小 。
  • 返回值,返回新的套接字描述符,出錯返回-1。進一步的通訊必須經過這個套接字。

Datagram 套接字不要求用accept()處理,由於接收方可能用監聽套接字當即處理這個請求。

 

18.6.5 函數connect()

函數原型: 

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

函數描述:

函數connect用於配置要鏈接的遠程IP地址和端口, 返回-1表示出錯,0表示成功。

 

18.6.6 函數gethostbyname()

函數原型: 

struct hostent *gethostbyname(const char *name);

 函數描述:

gethostbyname()函數是用來解析主機名。可能會使用DNS或者本地主機上的其餘解析機制。返回一個指向 struct hostent的指針,這個結構體描述一個IP主機。

函數gethostbyname使用以下參數:

  • name 指定主機名,例如 www.armfly.com
  • 出錯返回NULL指針,能夠經過檢查 h_errno 來肯定是臨時錯誤仍是未知主機。正確則返回一個有效的 struct hostent *。

注意:這個函數並非BSD Sockets嚴格的組成部分。這個函數多是過期了,新函數是getnameinfo(), 這個函數是基於addrinfo數據結構。

 

18.6.7 函數gethostbyaddr

函數原型: 

struct hostent *gethostbyaddr(const void *addr, int len, int type);

 

函數描述:

gethostbyaddr()函數是用來解析主機名和地址的。可能會使用DNS或者本地主機上的其餘解析機制。返回一個指向 struct hostent的指針,這個結構體描述一個IP主機。

函數gethostbyname使用以下參數:

  • addr 指向 struct in_addr的指針,包含主機的地址。
  • len 給出 addr的長度,以字節爲單位。
  • type 指定地址族類型 (好比 AF_INET)。
  • 返回值,出錯返回NULL指針,能夠經過檢查 h_errno 來肯定是臨時錯誤仍是未知主機。正確則返回一個有效的 struct hostent *。

注意:這個函數並非BSD Socket嚴格的組成部分。這個函數多是過期了,新函數是 getaddrinfo(),這個新函數是基於addrinfo數據結構。

 

18.7 總結

本章節就爲你們講解這麼多,更多BSD Sockets的相關知識須要你們查閱相關書籍進行學習,或者網上搜索相關資料進行學習。

相關文章
相關標籤/搜索