標籤:網路編程 網路編程api
htons、ntohs、htonl和ntohl函數
Linux提供了4個函數來完成主機位元組序和網路位元組序之間的轉換
#include <netinet/in.h>uint16_t htons(uint16_t host16bitvalue);uint32_t htonl(uint32_t host32bitvalue);uint16_t ntohs(uint16_t net16bitvalue);uint32_t ntohl(uint32_t net32bitvalue);
inet_aton、inet_addr和inet_ntoa函數
#include <arpa/inet.h>int inet_aton(const char *strptr, struct in_addr *addrptr);返回:若字元有效則為1,否則為0in_addr_t inet_addr(const char *strptr);返回:若字串有效則為32位二進位網路位元組序地址,否則為INADDR_NONEchar *inet_ntoa(struct in_addr inaddr);返回:指向一個點分十進位數串的地址
inet_aton將strptr所指向的字串轉換為32網路位元組序二進位值,並通過addrptr來儲存,成功返回1,失敗返回0。inet_addr進行與inet_aton相同的轉換,inet_addr不能處理點分十進位字串255.255.255.255,因為它的二進位值被用來只是函數返回失敗,但是,有的編譯器的INADDR_NONE不一定是這種。
注意,這3個函數操作的in_addr結構是網路位元組序的。進行網路二進位和點分字串轉換最好用inet_pton和inet_ntop函數,它們是與協議無關的。
inet_pton和inet_ntop函數
#include <arpa/inet.h>int inet_pton(int family, const char *strptr, void *addrptr);返回:成功為1,輸入不是有效運算式返回0,出錯為-1const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);返回:成功為指向結果的指標,出錯為NULL
這兩個函數對於IPv4和IPv6都適用,p代表運算式(presentation)和數值(numeric)。第一個函數嘗試轉化有strptr指標所指的字串,通過addptr指標存放二進位結果,成功返回1,如果對指定的family而言輸入的不是有效表達格式,那麼返回0
inet_ntop進行相反的操作,如果len的值太小,不足以存放運算式結果,則返回一個null 指標,共置error為ENOSPC
socket和connect函數
#include <sys/socket.h>int socket(int family, int type, int protocol);返回:成功返回非負描述符,出錯返回-1
socket函數指定期望的通訊協定類型(比如使用IPv4的TCP、使用IPv6的UDP、Unix域位元組流協議)和通訊端字類型(位元組流、資料報或原始通訊端)
----socket函數的family常值------------
family 說明
AF_INET IPv4協議
AF_INET6 IPv4協議
AF_LOCAL Unix協議域
AF_ROUTE 路由通訊端
AF_KEY 秘鑰通訊端
----------------------------------------------------
----socket函數的type常值----------------
SOCK_STREAM 位元組流通訊端
SOCK_DGRAM 資料通訊端
SOCK_SEQPACKET 有序分組通訊端
SOCK_RAW 原始通訊端
----------------------------------------------------
----socket函數的protocal常值----------
IPPROTO_CP TCP傳輸協議
IPPROTO_UDP UDP傳輸協議
IPPROTO_SCTP SCTP傳輸協議
----------------------------------------------------
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen);返回:成功為0,出錯-1
TCP客戶用connect函數來建立一個與TCP伺服器串連,sockfd是由socket函數返回的通訊端描述符,第二個、第三個參數分別是指向一個通訊端地址結構的指標和該結構的大小,通訊端結構必須含有伺服器的IP地址和連接埠號碼。如果connect失敗後,就必須close當前的通訊端描述符並重新調用socket。
bind和listen函數
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);返回:成功為0,出錯-1
bind函數把一個本地協議地址賦予一個通訊端,它只是把一個協議地址賦予一個通訊端,至於協議地址的含義則取決於協議本身。第二個參數指向協議地址結構的指標,第三個參數是協議地址的長度,對於TCP,調用bind函數可以指定一個連接埠號碼,或指定一個IP地址,或兩者都指定,也可以兩者都不指定。
#include <sys/socket.h>int listen(int sockfd, int backlog);返回:成功返回0,出錯-1
socket建立一個通訊端時,它被假設為一個主動通訊端,也就是說,它是一個將調用connect發起串連的一個客戶通訊端。listen函數把一個未串連的通訊端轉換為一個被動通訊端,指示核心應接受指向該通訊端的串連請求,調用listen函數將導致通訊端從CLOSEE狀態轉換到LISTEN狀態。第二個參數規定了核心應為相應通訊端排隊的最大串連個數。
(1)、未完成串連隊列:每一個這樣的SYN分節對應其中一項:已由某個客戶發出併到達伺服器,而伺服器正在等待完成相應的TCP三路握手過程。這些通訊端處於SYN_RCVD狀態。
(2)、已完成串連隊列:每個完成TCP三路握手過程的客戶對應其中一項,這些通訊端處於ESTABLISHED狀態。
accept函數
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);返回:成功返回已串連描述符(非負),出錯-1
如果accept成功,那麼其返回值是由核心自動產生的一個全新通訊端,代表與返回客戶的TCP串連,函數的第一個參數為監聽通訊端,返回值為已串連通訊端
伺服器、用戶端程式流程圖
伺服器用戶端簡單互動程式見:http://blog.csdn.net/u012796139/article/details/44984879
TCP狀態轉換圖
recv和send函數
TCP流資料讀寫
#include <sys/socket.h>ssize recv(int sockfd, void *buff, size_t nbytes, int flags);ssize send(int sockfd, void *buff, size_t nbytes, int flags);返回:成功為讀入或寫入的位元組數,出錯為-1
MSG_OOB 對於send,表明將要發送帶外資料,TCP串連上只有一個位元組可以作為帶外資料發送,對於recv,本標誌表明即將要讀入的是帶外資料而不是普通資料。
MSG_PEEK 該標誌適用於recv和recvfrom,它允許我們查看已可讀取的資料,而且在系統不在recv和recvfrom返回丟棄其這些資料
注意的是,flags參數只對send和recv的當前調用有效,當然也可以通過setsockopt系統調用永久性地修 改socket的某些屬性
recvfrom和sendto函數
#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);ssize_t recvto(int sockfd, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);返回:成功為讀或寫的位元組數,失敗為-1
前3個參數sockfd、buf、nbytes等同於read和write函數的3個參數:描述符、指向讀入或寫出緩衝區的指標和讀寫位元組數
recvmsg和sendmsg函數
#include <sys/socket.h>ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);返回:成功則為讀入或寫出位元組數,出錯-1
msg參數是msghdr結構體類型指標
struct msghdr{void *msg_name;//socket地址socklen_t msg_namelen;//socket地址長度struct iovec *msg_iov;//分散的記憶體塊int iovlen;//分散記憶體塊數量void *msg_control;//指向輔助資料的起始地址socklen_t msg_controllen;//輔助資料大小int msg_flags;//複製函數中的flags參數,並在調用過程中更新};struct iovec{void *iov_base;//記憶體起始地址size_t iov_len;//這塊記憶體長度}
iovec結構體封裝了一塊記憶體的起始地址和長度,msg_iovlen指定了這樣的iovec結構體有多少個,對於recvmsg而言,資料將被讀取並放在msg_iovlen塊分離的記憶體中,這些記憶體的位置和長度則由msg_iov指向的數組指定,這成為分散讀(scatter read);對於sendmsg而言,msg_iovlen塊分散記憶體中的資料將被一塊發送,這成為集中寫(gather write)
msg_control和msg_controllen用於輔助資料的發送。
msg_flags成員無需設定,它會複製recvmsg/sendmsg的flags參數的內容以影響資料讀寫過程。recvmsg還會在調用結束前,將某些更新後的標誌設定到msg_flags中。recvmsg/sendmsg的flags參數以及返回值的含義均與send/recv的flags參數返回值相同。
getsockname和getpeername函數
#include <sys/socket.h>int getsockname(int sockfd, struct sockaddr *localaddr, &addrlen);int getpeername(int sockfd, struct sockaddr *peeraddr, &addrlen);返回:成功為0, 出錯為-1
getsockname擷取sockfd對應的本端socket地址,並將其儲存於address參數指定的記憶體位址,該socket長度儲存於addrlen指向的變數中。getpeername擷取遠端的socket地址。
getsockope和setsockopt函數
#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t optlen);返回:成功為0,出錯-1
sockfd指向一個開啟的通訊端描述符,level(層級)指定系統中解釋選項的代碼或為通用通訊端代碼,或為某個特定於協議的代碼(例如IPv4、IPv6、TCP或SCTP)。optval是一個指向某個變數(*optval)的指標,setsockopt從*optval的大小有最後一個參數指定,它對於setsockopt是一個值參數,對於getsockopt是一個值-結果參數。
注意:通訊端選項粗分為兩大類型:一是啟用或禁止某個特性的二元選項(成為標誌選項),二是取得並返回我們可以設定或檢查的特定值的選項(稱為值選項)。標有 “標誌”的列指出一個選項是否是標誌選項,當給這些標誌選項調用getsockopt函數時,*optval是一個整數,*optval中返回的為0表示相應選項被禁止,不為0表示相應選項被啟動。類似的,setsockopt需要一個不為0的*optval來啟動選項,一個為0的*optval來禁止選項。如果標誌中沒有”.”,那麼相應選項用於在使用者進程和系統之間傳遞所指定的資料類型的值。
SO_RCVBUF和SO_SNDBUF通訊端選項
每個通訊端都有一個發送緩衝區和一個接收緩衝區,接收緩衝區被TCP、UDP和SCTP用來儲存收到的資料,直到由應用進程來讀取,對於TCP來說,通訊端接收緩衝區中可用的空間大小限制了TCP通告視窗的大小。TCP通訊端緩衝區不可能溢出,因為不允許對端發出超過該視窗大小的資料,本端TCP將丟棄他們。對於UDP是沒有流量控制的:較快的發送端可以很容易地淹沒較慢得接收端,導致接收端的UDP丟棄資料,事實上較快的發送端甚至可以淹沒原生網路介面,導致資料報被本機丟棄。
當設定TCP通訊端接收緩衝區的大小時,函數調用的順序很重要,因為TCP視窗規模選項是在建立串連時用SYN分節與對端交換得到的。對於用戶端,這意味著SO_RCVBUF選項必須在調用connect之前設定;對於伺服器,這意味著該選項必須在調用listen之前對通訊端進行設定。對已串連的通訊端設定該選項對可能存在的視窗規模選項沒有任何影響,因為accept直到TCP三向交握完成才會建立並返回已串連通訊端,這就是必須給監聽通訊端進行該選項設定的原因,通訊端緩衝區大小是由新建立的已串連通訊端從監聽通訊端繼承來的。
gethostbyname函數
#include <netdb.h>struct hostent *gethostbyname(const char *hostname);返回:成功為非null 指標,出錯為NULL並設定h_errno
函數返回的非null 指標結構如下的hostent結構
struct hostent{char *h_name;char **h_aliases;int h_addrtype;int h_length; /* length of address: 4 */char **h_addr_list;}
按照DNS的說法,gethostbyname執行的是A記錄的查詢(DNS的資源記錄有A、AAAA、PTR、MX、CNAME等),它只能返回IPv4地址,注意,該函數返回的是struct hostent *結構
gethostbyaddr函數
#include <netdb.h>struct hostent *gethostbyaddr(const char *addr, socklen_t len, int fanily);返回:成功為非null 指標,出錯為NULL共置h_errno
addr參數其實並不是char *結構,而是一個指向存放IPv4地址的in_addr結構的指標,len參數是這個結構的大小,對於IPv4,family參數為AF_INET
getservbyname和getservbyport函數
#include <netdb.h>struct servent *getservbyneme(const char *servname, const char *protoname);struct servent *getservbyport(int port, const char *protoname);返回:成功為非null 指標,出錯為NULL
函數返回的非null 指標指向如下結構:
struct servent{char *s_name;char *s_aliases; /* alias list */int s_port;char *s_proto;} 從名字到連接埠號碼的映射關係一般儲存在一個檔案中(通常是/etc/services)
getservbyname的參數servname必須指定,如果同時指定了protoname(為非null 指標),那麼指定的服務必須和協議匹配。如果protoname未指定,而servname指向服務支援多個協議,那麼返回那個連接埠號碼取決於實現,通常沒有關係,因為多個協議相同的服務一般有相同的TCP和UDP連接埠號碼,但這點並沒有保證
getservbyport的port參數必須是網路序
使用執行個體:
struct servent *pserv;
pserv = getservbyname(“ftp”, “tcp”);
pserv = getservbyport(htons(80), “tcp”);
參考:
1、《UNIX網路編程》第4章 第11章
2、《Linux高效能伺服器編程》第5章 Linux網路編程基礎API
3、伺服器、用戶端簡單互動程式 http://blog.csdn.net/u012796139/article/details/44984879
網路編程API-上 (基本API)