經常使用socket函數詳解

經常使用socket函數詳解linux

關於socket函數,每一個的意義和基本功能都知道,但每次使用都會去百度,參數究竟是什麼,返回值表明什麼意義,就是說用的少,也記得不夠精確。每次都查半天,常常煩惱於此。索性都弄得清楚、通透,並記錄下來,一來便於本身記憶,再者以防往後查閱、回顧。編程

 

主要介紹:socket、bind、listen、connect、accept、send、sendto、recv、recvfrom、close、shutdownwindows

 

網絡中的進程是經過socket來通訊的,那什麼是socket呢?socket起源於Unix,而Unix/Linux基本哲學之一就是「一切皆文件」,均可以用「打開open –> 讀寫write/read –> 關閉close」模式來操做。個人理解就是Socket就是該模式的一個實現,socket便是一種特殊的文件。緩存

其在linux和windows環境下的頭文件主要是:#include<sys/socket.h>#include<WinSock2.h>服務器

下面較爲詳細的介紹各個函數的使用方法,及返回值判斷和處理。另外,若想對函數調用後內核的詳細動做過程,可參考UNIX網絡編程第一卷或TCPIP詳解第二卷。網絡

 

1.  socket數據結構

intsocket(int domain,int type, int protocol)dom

_________________________返回值:非負描述符 – 成功,-1 - 出錯socket

其中:tcp

family指明瞭協議族/域,一般AF_INET、AF_INET六、AF_LOCAL等;

type是套接口類型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;

protocol通常取爲0。成功時,返回一個小的非負整數值,與文件描述符相似。

       對於windows環境下,在調用該函數以前需首先調用WSAStartup函數完成對Winsock服務的初始化,如

#include<WinSock2.h>

WSADATA wdata;

if ( WSAStartup(MAKEWORD(2,2), &wdata) !=0 ){

return INVALID_SOCKET;

}

後面便可調用socket函數,參數意義與linux環境一致。

 

 

2.  bind

intbind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen)

_________________________返回值:0 – 成功,-1 - 出錯

       當socket函數返回一個描述符時,只是存在於其協議族的空間中,並無分配一個具體的協議地址(這裏指IPv4/IPv6和端口號的組合),bind函數能夠將一組固定的地址綁定到sockfd上。

其中:

sockfd是socket函數返回的描述符;

myaddr指定了想要綁定的IP和端口號,均要使用網絡字節序-即大端模式;

addrlen是前面struct sockaddr(與sockaddr_in等價)的長度。

爲了統一地址結構的表示方法,統一接口函數,使得不一樣的地址結構能夠被bind()、connect()、recvfrom()、sendto()等函數調用。但通常的編程中並不直接對此數據結構進行操做,而使用另外一個與之等價的數據結構sockaddr_in。

       一般服務器在啓動的時候都會綁定一個衆所周知的協議地址,用於提供服務,客戶就能夠經過它來接連服務器;而客戶端能夠指定IP或端口也能夠都不指定,未分配則系統自動分配。這就是爲何一般服務器端在listen以前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。

       Windows下的版本:

    Int bind( IN SOCKET s, IN const struct sockaddr FAR * name, IN int namelen);

 

 

3.  listen

intlisten(int sockfd,int backlog)

______________________返回值:0 – 成功,-1 - 出錯

                                                                                                                                                                                                       (截圖來自:《UNIX網絡編程第一卷》)

兩個隊列之和數量不得超過backlog.

 

 

4.  connect

intconnect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen)

______________________返回值:0 – 成功,-1 - 出錯

       經過此函數創建於TCP服務器的鏈接,實際是發起三次握手過程,僅在鏈接成功或失敗後返回。參數sockfd是本地描述符,addr爲服務器地址,addrlen是socket地址長度。

UDP的connect函數,結果與tcp調用不相同,沒有三次握手過程。內核只是記錄對方的ip和端口號,他們包含在傳遞給connect的套接口地址結構中,並當即返回給調用進程。

 

 

5.  accept

intaccept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

______________________返回值:非負描述符 – 成功,-1 - 出錯

                                                                                                                                                                           (截圖來自:《UNIX網絡編程第一卷》)

 

 

6.  send

ssize_tsend(int sockfd,constvoid *buf, size_t len,int flags)

返回值:

>0 – 成功拷貝至發送緩衝區的字節數(可能小於len),

-1 – 出錯,並置錯誤號errno.

Windows版本:

int send(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags)

       失敗時返回 -1/SOCKET_ERROR

其中:

       sockfd:發送端套接字描述符(非監聽描述符)

       buf:應用要發送數據的緩存

       len:實際要發送的數據長度

       flag:通常設置爲0

每一個TCP套接口都有一個發送緩衝區,它的大小能夠用SO_SNDBUF這個選項來改變。調用send函數的過程,實際是內核將用戶數據拷貝至TCP套接口的發送緩衝區的過程:若len大於發送緩衝區大小,則返回-1;不然,查看緩衝區剩餘空間是否容納得下要發送的len長度,若不夠,則拷貝一部分,並返回拷貝長度(指的是非阻塞send,若爲阻塞send,則必定等待全部數據拷貝至緩衝區才返回,所以阻塞send返回值一定與len相等);若緩衝區滿,則等待發送,有剩餘空間後拷貝至緩衝區;若在拷貝過程出現錯誤,則返回-1。關於錯誤的緣由,查看errno的值。

       若是send在等待協議發送數據時出現網絡斷開的狀況,則會返回-1。注意:send成功返回並不表明對方已接收到數據,若是後續的協議傳輸過程當中出現網絡錯誤,下一個send便會返回-1發送錯誤。TCP給對方的數據必須在對方給予確認時,方可刪除發送緩衝區的數據。不然,會一直緩存在緩衝區直至發送成功(TCP可靠數據傳輸決定的)。

 

 

7.  sendto

ssize_t sendto(int sockfd,const void *buf, size_t len, int flags,

                      const struct sockaddr *dst_addr, socklen_t addrlen);

       windows版本:

int sendto(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags, IN const struct sockaddr FAR * to, IN int tolen)

 

一般用於UDP套接口,用數據報方式進行數據傳輸。因爲無鏈接的數據報模式下,沒有創建鏈接,需指明目的地址,addrlen一般爲sizeof(sockaddr)的長度。成功時返回發送的字節數,失敗返回-1。

       當本地與不一樣目的地址通訊時,只需指定目的地址,可以使用同一個UDP套接口描述符sockfd,而TCP要預先創建鏈接,每一個鏈接都會產生不一樣的套接口描述符,體如今:客戶端要使用不一樣的fd進行connect,服務端每次accept產生不一樣的fd。

       由於UDP沒有真正的發送緩衝區,由於是不可靠鏈接,沒必要保存應用進程的數據拷貝,應用進程中的數據在沿協議棧向下傳遞時,以某種形式拷貝到內核緩衝區,當數據鏈路層把數據傳出後就把內核緩衝區中數據拷貝刪除。所以它不須要一個發送緩衝區。寫UDP套接口的sendto/write返回表示應用程序的數據或數據分片已經進入鏈路層的輸出隊列,若是輸出隊列沒有足夠的空間存放數據,將返回錯誤ENOBUFS.

 

關於TCP/UDP套接口的發送緩衝區理解:

(1)下圖展現了應用進程寫數據到TCP套接口的過程:

每個TCP套接口有一個發送緩衝區,咱們能夠用SO_SNDBUF套接口選項來改變這個緩衝區的大小。當應用程序調用write時,內核從應用程序進程的緩衝區中拷貝全部數據到套接口的發送緩衝區。若是套接口的發送緩衝區容不下應用程序的全部數據(或是應用程序的緩衝區大於套接口發送緩衝區,或是套接口發送緩衝區還有其餘數據),應用進程將被掛起(睡眠)。這裏假設套接口是阻塞的,它是一般的缺省設置(還有非阻塞的套接口)。內核將不從write系統調用返回,直到應用程序緩衝區中的全部數據都拷貝到套接口發送緩衝區。所以從寫一個TCP套接口的write調用成功返回僅僅表示咱們能夠從新使用應用進程的緩衝區。它並不告訴咱們對端的TCP或應用程序已接收到數據。

TCP取套接口發送緩衝區的數據並把它發送給對端TCP,其過程基於TCP數據傳送的全部規則。對端TCP必需確認收到數據,只有收到對端的ACK,本端TCP才能刪除套接口發送緩衝區中已確認的數據。TCP必需保留數據拷貝直到對端確認爲止。

TCP以MSS大小的或更小的塊把數據傳遞給IP,同時給每一個數據塊安上一個TCP頭部以構成TCP分節,其中的MSS是由對端通告的,當對端未通告時就用536這個值(IPv4的最小重組緩衝區字節數576減去IPv4頭部字節20和TCP頭部字節數20)。IP給每一個TCP分節安上IP頭部以構成IP數據報,查找其宿IP地址的路由表項以肯定外出接口,而後把數據報傳遞給相應的數據鏈路。IP可能在把數據報傳遞給數據鏈路以前將其分片,不過咱們已經談到MSS選項的目的之一就是試圖避免分片,而較新的實現又使用了路徑MTU發現功能。每一個數據鏈路都有一個輸出隊列,若是該隊列已滿,那麼新到的分組將被丟棄,並沿協議棧向上返回一個錯誤,從鏈路層到IP層,再從IP層到TCP層。TCP將注意到這個錯誤,並在之後某個時刻重傳相應分片。應用進程並不知道這種暫時狀況。

      

(2)下圖展現了應用進程寫數據到UDP套接口的過程:

這一次咱們展現的套接口發送緩衝區用虛線框,由於它並不存在。UDP套接口有發送緩衝區大小(咱們能夠用SO_SNDBUF套接口選項修改),不過它僅僅是寫到套接口的UDP數據報的大小上限。若是應用進程寫一個大於套接口發送緩衝區大小的數據包,內核將返回一個EMSGSIZE錯誤。既然UDP是不可靠的,它沒必要保存應用程序的數據拷貝,所以無需一個真正的發送緩衝區。(應用進程的數據在沿協議向下傳遞時,以某種形式拷貝到內核的緩衝區,然而數據鏈路層在送出這些數據後將丟棄該拷貝)

UDP簡單地給用戶數據報安上它的8個字節的頭部以構成UDP數據報,而後傳遞給IP。IPv4或IPv6給UDP數據報安上相應的IP頭部以構成IP數據報,執行路由操做肯定外出接口,而後直接把數據包加入數據鏈路層輸出隊列(若是適合於MTU),或者分片後再把每一個片加入數據鏈路層的輸出隊列。若是某個UDP應用進程發送大數據報,那麼它比TCP應用進程更有可能分片,由於TCP會把應用數據劃分紅MSS大小的塊,而UDP卻沒有對等的手段。

從寫UDP套接口的write調用成功地返回表示用戶寫入的數據報或其全部片斷已被加入數據鏈路層的輸出隊列。若是該隊列沒有足夠的空間存放該數據報或它的某個片斷,內核一般將給應用程序返回一個ENOBUFS錯誤。

 

8.  recv

ssize_t recv(int sockfd,void *buf, size_t len,int flags)

其中:

sockfd:接收端套接字描述符;

buf:指定緩衝區地址,用於存儲接收數據;

len:指定的用於接收數據的緩衝區長度;

flags:通常指定爲0

表示從接收緩衝區拷貝數據。成功時,返回拷貝的字節數,失敗返回-1。阻塞模式下,recv/recvfrom將會阻塞到緩衝區裏至少有一個字節(TCP)/至少有一個完整的UDP數據報才返回,沒有數據時處於休眠狀態。若非阻塞,則當即返回,有數據則返回拷貝的數據大小,不然返回錯誤-1,置錯誤碼爲EWOULDBLOCK。

 

9.  recvfrom

ssize_t recvfrom(int sockfd,void *buf, size_t len, int flags,

struct sockaddr *src_addr, socklen_t *addrlen)

windows版本:

int recvfrom(IN SOCKET s, OUT char FAR * buf, IN int len, IN int flags, OUT struct sockaddr FAR * from,IN OUT int FAR * fromlen)

着重強調參數:

       sockfd:接收端套接字描述

       buf:用於接收數據的應用緩衝區地址

       len:指名緩衝區大小

       flags:一般爲0

       src_addr:數據來源端的地址

       addrlen:src_addr地址的長度

注意後兩個參數是輸出參數,其中addrlen既是輸入又是輸出參數,即值-結果參數,須要在調用時,指明src_addr的長度。另外,若是不關心數據發送端的地址,能夠將後二者均設置爲NULL。

 

10. close

close缺省功能是將套接字做「已關閉」標記,並當即返回到調用進程,該套接字描述符不能再爲該進程所用:即不能做爲read和write(send和recv)的參數,可是TCP將試着發送發送緩衝區內已排隊待發的數據,而後按正常的TCP鏈接終止序列進行操做(斷開鏈接4次握手-以FIN爲首的4個TCP分節)。

 

11. shutdown

shutdown不只能夠靈活控制關閉鏈接的讀、寫或讀寫功能,並且會當即執行相應的斷開動做(發送終止鏈接的FIN分節等),此時不論有多少進程共享此套接字描述符,都將不能再進行收發數據。

相關文章
相關標籤/搜索