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講解