網絡編程4:Linux提供的API簡析(針對Socket服務器和客戶端的開發步驟)

Linux提供的API簡析(針對Socket服務器和客戶端的開發步驟)

1.鏈接協議:(TCP/UDP)web

//建立套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
返回sockfd---失敗返回-1

domain:
指明所使用的協議族,一般爲AF_INET,表示互聯網協議族(TCP/IP協議族);編程

AF_INET IPV4 因特網域
AF_INET6 IPV6 因特網域
AF_UNIX unix 域
AF_ROUTE 路由套接字
AF_KEY 密鑰套接字
AF_UNSPEC 未指定服務器

type參數指定socket的類型:
SOCK_STREAM:
流式套接字提供可靠的,面向鏈接的通訊流,它使用TCP協議,從而保證了數據傳輸的正確性和順序性
SOCK_DGRAM:
數據報套接字定義了一種無鏈接的服務器,數據經過相互獨立的報文進行傳輸,是無序的,並不保證可靠性,無差錯的,它使用數據報協議UDP
SOCK_RAW
容許程序使用底層協議,原始套接字運行對底層協議如IP或者ICMP進行直接訪問,功能強大但使用較爲不便,主要用於一些協議的開發網絡


protocol
一般賦值爲「0
0選擇type類型對應的默認協議
== IPPROTO_TCP TCP 傳輸協議==
IPPROTO_UDP UDP 傳輸協議
IPPROTO_SCTP SCTP 傳輸協議
IPPROTO_TIPC TIPC 傳輸協議dom

2.IP號端口號與相應描述字賦值函數:bind()函數socket

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

bind()函數把一個地址族中的特定地址賦給socket。例如對應AF_INET、AF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket。svg

功能:用於綁定IP地址和端口號到socketfd
參數:
sockfd:即socket描述字,它是經過socket()函數建立了,惟一標識一個socket。bind()函數就是將給這個描述字綁定一個名字。
addr:一個const struct sockaddr *指針,指向要綁定給sockfd的協議地址
addrlen:對應的是地址的長度。函數

協議地址結構根據地址建立socket時的地址協議族的不一樣而不一樣,如ipv4對應的是:ui

struct sockaddr_in { 
 
  
    sa_family_t    sin_family; //協議族
    in_port_t      sin_port;   //端口號
    struct in_addr sin_addr;   //IP地址結構體
};
struct in_addr { 
 
  
    uint32_t       s_addr;     /* address in network byte order */
};

一般服務器在啓動的時候都會綁定一個衆所周知的地址==(如ip地址+端口號)==,用於提供服務,客戶就能夠經過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。這就是爲何一般服務器端在listen以前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。spa

3.listen()監聽函數、connect()鏈接函數

若是做爲一個服務器,在調用socket()、bind()以後就會調用listen()來監聽這個socket,若是客戶端這時調用connect()發出鏈接請求,服務器端就會接收到這個請求。

#include <sys/types.h> 
#include <sys/socket.h>
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen函數的第一個參數即爲要監聽的socket描述字,第二個參數爲相應socket能夠排隊的最大鏈接個數。socket()函數建立的socket默認是一個主動類型的,listen函數將socket變爲被動類型的,等待客戶的鏈接請求。

connect函數的第一個參數即爲客戶端的socket描述字,第二參數爲服務器的socket地址,第三個參數爲socket地址的長度。客戶端經過調用connect函數來創建與TCP服務器的鏈接。
  
4.accept()接收請求函數

TCP服務器端依次調用socket()、bind()、listen()以後,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()以後就向TCP服務器發送了一個鏈接請求。TCP服務器監聽到這個請求以後,就會調用accept()函數取接收請求,這樣鏈接就創建好了。以後就能夠開始網絡I/O操做了,即類同於普通文件的讀寫I/O操做。

#include <sys/types.h> 
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回鏈接connect_fd

函數的三個參數分別爲:

sockfd: 就是上面解釋中的監聽套接字,這個套接字用來監聽一個端口,當有一個客戶與服務器鏈接時,它使用這個一個端口號,而此時這個端口號正與這個套接字關聯。固然客戶不知道套接字這些細節,它只知道一個地址和一個端口號。
addr: 這是一個結果參數,它用來接受一個返回值,這返回值指定客戶端的地址,固然這個地址是經過某個地址結構來描述的,用戶應該知道這一個什麼樣的地址結構。若是對客戶的地址不感興趣,那麼能夠把這個值設置爲NULL。
len: 如同你們所認爲的,它也是結果的參數,用來接受上述addr的結構的大小的,它指明addr結構所佔有的字節個數。一樣的,它也能夠被設置爲NULL。

若是accept成功返回,則服務器與客戶已經正確創建鏈接了,此時服務器經過accept返回的套接字來完成與客戶的通訊。

注意
accept默認會阻塞進程,直到有一個客戶鏈接創建後返回,它返回的是一個新可用的套接字,這個套接字是鏈接套接字。

此時咱們須要區分兩種套接字:

監聽套接字: 監聽套接字正如accept的參數sockfd,它是監聽套接字,在調用listen函數以後,是服務器開始調用socket()函數生成的,稱爲監聽socket描述字(監聽套接字)
鏈接套接字:一個套接字會從主動鏈接的套接字變身爲一個監聽套接字;而accept函數返回的是已鏈接socket描述字(一個鏈接套接字),它表明着一個網絡已經存在的點點鏈接。

一個服務器一般一般僅僅只建立一個監聽socket描述字,它在該服務器的生命週期內一直存在。內核爲每一個由服務器進程接受的客戶鏈接建立了一個已鏈接socket描述字,當服務器完成了對某個客戶的服務,相應的已鏈接socket描述字就被關閉。

鏈接套接字socketfd_new 並無佔用新的端口與客戶端通訊,依然使用的是與監聽套接字socketfd同樣的端口號。
  
5.read()、write()等函數

網絡I/O操做有下面幾組:

read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()

6.close函數

int close(int fd);

close一個TCP socket的缺省行爲時把該socket標記爲以關閉,而後當即返回到調用進程。該描述字不能再由調用進程使用,也就是說不能再做爲read或write的第一個參數。

注意:close操做只是使相應socket描述字的引用計數-1,只有當引用計數爲0的時候,纔會觸發TCP客戶端向服務器發送終止鏈接請求。

7.地址轉換API:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
//把字符串造成的「192.168.1.123」轉爲網絡能識別的格式
char *inet_ntoa(struct in_addr in);
//把網絡格式的ip地址轉化爲字符串形式

8.字節序轉換API:

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);//返回網絡字節序的值
uint16_t htons(uint16_t hostshort);//返回網絡字節序的值
uint32_t ntohl(uint32_t netlong);//返回主機字節序的值uint32_t
uint16_t ntohs(uint16_t netshort);//返回主機字節序的值

h表明host,n表明net,s表明short(兩個字節),l表明long(4個字節);經過上面4個函數能夠實現主機字節序和網絡字節序之間的轉換。有時能夠用INADDR_ANY, INADDR_ANY指定地址讓操做系統本身獲取。

經過以上API能夠完成客戶端和服務端之間的代碼編寫

代碼實現見下一節網絡編程5講解