1、Socket簡介服務器
Socket是進程通信的一種方式,即調用這個網絡庫的一些API函數實現分佈在不一樣主機的相關進程之間的數據交換。網絡
幾個定義:併發
(1)IP地址:即依照TCP/IP協議分配給本地主機的網絡地址,兩個進程要通信,任一進程首先要知道通信對方的位置,即對方的IP。異步
(2)端口號:用來辨別本地通信進程,一個本地的進程在通信時均會佔用一個端口號,不一樣的進程端口號不一樣,所以在通信前必需要分配一個沒有被訪問的端口號。socket
(3)鏈接:指兩個進程間的通信鏈路。函數
(4)半相關:網絡中用一個三元組能夠在全局惟一標誌一個進程:指針
(協議,本地地址,本地端口號)接口
這樣一個三元組,叫作一個半相關,它指定鏈接的每半部分。隊列
(4)全相關:一個完整的網間進程通訊須要由兩個進程組成,而且只能使用同一種高層協議。也就是說,不可能通訊的一端用TCP協議,而另外一端用UDP協議。所以一個完整的網間通訊須要一個五元組來標識:進程
(協議,本地地址,本地端口號,遠地地址,遠地端口號)
這樣一個五元組,叫作一個相關(association),即兩個協議相同的半相關才能組合成一個合適的相關,或徹底指定組成一鏈接。
2、客戶/服務器模式
在TCP/IP網絡應用中,通訊的兩個進程間相互做用的主要模式是客戶/服務器(Client/Server, C/S)模式,即客戶向服務器發出服務請求,服務器接收到請求後,提供相應的服務。客戶/服務器模式的創建基於如下兩點:
(1)首先,創建網絡的原由是網絡中軟硬件資源、運算能力和信息不均等,須要共享,從而造就擁有衆多資源的主機提供服務,資源較少的客戶請求服務這一非對等做用。
(2)其次,網間進程通訊徹底是異步的,相互通訊的進程間既不存在父子關係,又不共享內存緩衝區,所以須要一種機制爲但願通訊的進程間創建聯繫,爲兩者的數據交換提供同步,這就是基於客戶/服務器模式的TCP/IP。
服務器端:
其過程是首先服務器方要先啓動,並根據請求提供相應服務:
(1)打開一通訊通道並告知本地主機,它願意在某一公認地址上的某端口(如FTP的端口可能爲21)接收客戶請求;
(2)等待客戶請求到達該端口;
(3)接收到客戶端的服務請求時,處理該請求併發送應答信號。接收到併發服務請求,要激活一新進程來處理這個客戶請求(如UNIX系統中用fork、exec)。新進程處理此客戶請求,並不須要對其它請求做出應答。服務完成後,關閉此新進程與客戶的通訊鏈路,並終止。
(4)返回第(2)步,等待另外一客戶請求。
(5)關閉服務器
客戶端:
(1)打開一通訊通道,並鏈接到服務器所在主機的特定端口;
(2)向服務器發服務請求報文,等待並接收應答;繼續提出請求......
(3)請求結束後關閉通訊通道並終止。
從上面所描述過程可知:
(1)客戶與服務器進程的做用是非對稱的,所以代碼不一樣。
(2)服務器進程通常是先啓動的。只要系統運行,該服務進程一直存在,直到正常或強迫終止。
介紹完基礎知識,下面就介紹一些API函數:
建立套接字──socket()
應用程序在使用套接字前,首先必須擁有一個套接字,系統調用socket()嚮應用程序提供建立套接字的手段,其調用格式以下:
SOCKET PASCAL FAR socket(int af, int type, int protocol);
該調用要接收三個參數:af、type、protocol。參數af指定通訊發生的區域:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中僅支持AF_INET,它是網際網區域。所以,地址族與協議族相同。參數type 描述要創建的套接字的類型。這裏分三種:
(1)一是TCP流式套接字(SOCK_STREAM)提供了一個面向鏈接、可靠的數據傳輸服務,數據無差錯、無重複地發送,且按發送順序接收。內設流量控制,避免數據流超限;數據被看做是字節流,無長度限制。文件傳送協議(FTP)即便用流式套接字。
(2)二是數據報式套接字(SOCK_DGRAM)提供了一個無鏈接服務。數據包以獨立包形式被髮送,不提供無錯保證,數據可能丟失或重複,而且接收順序混亂。網絡文件系統(NFS)使用數據報式套接字。
(3)三是原始式套接字(SOCK_RAW)該接口容許對較低層協議,如IP、ICMP直接訪問。經常使用於檢驗新的協議實現或訪問現有服務中配置的新設備。
參數protocol說明該套接字使用的特定協議,若是調用者不但願特別指定使用的協議,則置爲0,使用默認的鏈接模式。根據這三個參數創建一個套接字,並將相應的資源分配給它,同時返回一個整型套接字號。所以,socket()系統調用實際上指定了相關五元組中的「協議」這一元。
指定本地地址──bind()
當一個套接字用socket()建立後,存在一個名字空間(地址族),但它沒有被命名。bind()將套接字地址(包括本地主機地址和本地端口地址)與所建立的套接字號聯繫起來,即將名字賦予套接字,以指定本地半相關。其調用格式以下:
int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen);
參數s是由socket()調用返回的而且未做鏈接的套接字描述符(套接字號)。參數name 是賦給套接字s的本地地址(名字),其長度可變,結構隨通訊域的不一樣而不一樣。namelen代表了name的長度。若是沒有錯誤發生,bind()返回0。不然返回SOCKET_ERROR。
創建套接字鏈接──connect()與accept()
這兩個系統調用用於完成一個完整相關的創建,其中connect()用於創建鏈接。accept()用於使服務器等待來自某客戶進程的實際鏈接。
connect()的調用格式以下:
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);
參數s是欲創建鏈接的本地套接字描述符。參數name指出說明對方套接字地址結構的指針。對方套接字地址長度由namelen說明。
若是沒有錯誤發生,connect()返回0。不然返回值SOCKET_ERROR。在面向鏈接的協議中,該調用致使本地系統和外部系統之間鏈接實際創建。
因爲地址族總被包含在套接字地址結構的前兩個字節中,並經過socket()調用與某個協議族相關。所以bind()和connect()無須協議做爲參數。
accept()的調用格式以下:
SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);
參數s爲本地套接字描述符,在用作accept()調用的參數前應該先調用過listen()。addr 指向客戶方套接字地址結構的指針,用來接收鏈接實體的地址。addr的確切格式由套接字建立時創建的地址族決定。addrlen 爲客戶方套接字地址的長度(字節數)。若是沒有錯誤發生,accept()返回一個SOCKET類型的值,表示接收到的套接字的描述符。不然返回值INVALID_SOCKET。
accept()用於面向鏈接服務器。參數addr和addrlen存放客戶方的地址信息。調用前,參數addr 指向一個初始值爲空的地址結構,而addrlen 的初始值爲0;調用accept()後,服務器等待從編號爲s的套接字上接受客戶鏈接請求,而鏈接請求是由客戶方的connect()調用發出的。當有鏈接請求到達時,accept()調用將請求鏈接隊列上的第一個客戶方套接字地址及長度放入addr 和addrlen,並建立一個與s有相同特性的新套接字號。新的套接字可用於處理服務器併發請求。
四個套接字系統調用,socket()、bind()、connect()、accept(),能夠完成一個徹底五元相關的創建。socket()指定五元組中的協議元,它的用法與是否爲客戶或服務器、是否面向鏈接無關。bind()指定五元組中的本地二元,即本地主機地址和端口號,其用法與是否面向鏈接有關:在服務器方,不管是否面向鏈接,均要調用bind(),若採用面向鏈接,則能夠不調用bind(),而經過connect()自動完成。若採用無鏈接,客戶方必須使用bind()以得到一個惟一的地址。
監聽鏈接──listen()
此調用用於面向鏈接服務器,代表它願意接收鏈接。listen()需在accept()以前調用,其調用格式以下:
int PASCAL FAR listen(SOCKET s, int backlog);
參數s標識一個本地已創建、還沒有鏈接的套接字號,服務器願意從它上面接收請求。backlog表示請求鏈接隊列的最大長度,用於限制排隊請求的個數,目前容許的最大值爲5。若是沒有錯誤發生,listen()返回0。不然它返回SOCKET_ERROR。
listen()在執行調用過程當中可爲沒有調用過bind()的套接字s完成所必須的鏈接,並創建長度爲backlog的請求鏈接隊列。
調用listen()是服務器接收一個鏈接請求的四個步驟中的第三步。它在調用socket()分配一個流套接字,且調用bind()給s賦於一個名字以後調用,並且必定要在accept()以前調用。
數據傳輸──send()與recv()
當一個鏈接創建之後,就能夠傳輸數據了。經常使用的系統調用有send()和recv()。
send()調用用於s指定的已鏈接的數據報或流套接字上發送輸出數據,格式以下:
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
參數s爲已鏈接的本地套接字描述符。buf 指向存有發送數據的緩衝區的指針,其長度由len 指定。flags 指定傳輸控制方式,如是否發送帶外數據等。若是沒有錯誤發生,send()返回總共發送的字節數。不然它返回SOCKET_ERROR。
recv()調用用於s指定的已鏈接的數據報或流套接字上接收輸入數據,格式以下:
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
參數s 爲已鏈接的套接字描述符。buf指向接收輸入數據緩衝區的指針,其長度由len 指定。flags 指定傳輸控制方式,如是否接收帶外數據等。若是沒有錯誤發生,recv()返回總共接收的字節數。若是鏈接被關閉,返回0。不然它返回SOCKET_ERROR。
輸入/輸出多路複用──select()
select()調用用來檢測一個或多個套接字的狀態。對每個套接字來講,這個調用能夠請求讀、寫或錯誤狀態方面的信息。請求給定狀態的套接字集合由一個fd_set結構指示。在返回時,此結構被更新,以反映那些知足特定條件的套接字的子集,同時, select()調用返回知足條件的套接字的數目,其調用格式以下:
int PASCAL FAR select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout);
參數nfds指明被檢查的套接字描述符的值域,此變量通常被忽略。
參數readfds指向要作讀檢測的套接字描述符集合的指針,調用者但願從中讀取數據。參數writefds 指向要作寫檢測的套接字描述符集合的指針。exceptfds指向要檢測是否出錯的套接字描述符集合的指針。timeout指向select()函數等待的最大時間,若是設爲NULL則爲阻塞操做。select()返回包含在fd_set結構中已準備好的套接字描述符的總數目,或者是發生錯誤則返回SOCKET_ERROR。
關閉套接字──closesocket()
closesocket()關閉套接字s,並釋放分配給該套接字的資源;若是s涉及一個打開的TCP鏈接,則該鏈接被釋放。closesocket()的調用格式以下:
BOOL PASCAL FAR closesocket(SOCKET s);
參數s待關閉的套接字描述符。若是沒有錯誤發生,closesocket()返回0。不然返回值SOCKET_ERROR。