C++ POSIX API超詳細(xì)分析
1.網(wǎng)絡(luò)通信
1.消息傳遞(管道、FIFO、消息隊(duì)列)
2.同步(互斥量、條件變量、讀寫(xiě)鎖、文件和寫(xiě)記錄鎖、信號(hào)量)
3.共享內(nèi)存(匿名的和具名的)
使用TCP/IP協(xié)議 通過(guò)socket完成
2.posix API
目的:實(shí)現(xiàn)不同系統(tǒng)上的源代碼的可移植性。
舉例:linux和windows都要實(shí)現(xiàn)基本的posix標(biāo)準(zhǔn),linux把fork函數(shù)封裝成posix_fork(隨便說(shuō)的),windows把creatprocess函數(shù)也封裝成posix_fork,都聲明在unistd.h里。這樣,程序員編寫(xiě)普通應(yīng)用時(shí)候,只用包含unistd.h,調(diào)用
3.POSIX網(wǎng)絡(luò)API
4.函數(shù)內(nèi)部過(guò)程解析
4.1 socket套接字創(chuàng)建
int socket(int domain, int type, int protocol); //參數(shù)分別是地址族、 套接字類型和協(xié)議 //AF_INET SOCK_STREAM 可默認(rèn)為0 //IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等
4.2 bind 綁定端口
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:即socket描述字
addr:一個(gè)const struct sockaddr *指針,指向要綁定給sockfd的協(xié)議地址。
struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0 監(jiān)聽(tīng)時(shí),監(jiān)聽(tīng)所有的地址 server_addr.sin_port = htons(port);
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET 協(xié)議族*/ in_port_t sin_port; /* port in network byte order 端口號(hào)*/ struct in_addr sin_addr; /* internet address IP地址*/ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
通常服務(wù)器在啟動(dòng)的時(shí)候都會(huì)綁定一個(gè)眾所周知的地址(如ip地址+端口號(hào)),用于提供服務(wù),客戶就可以通過(guò)它來(lái)接連服務(wù)器;而客戶端就不用指定,有系統(tǒng)自動(dòng)分配一個(gè)端口號(hào)和自身的ip地址組合。這就是為什么通常服務(wù)器端在listen之前會(huì)調(diào)用bind(),而客戶端就不會(huì)調(diào)用,而是在connect()時(shí)由系統(tǒng)隨機(jī)生成一個(gè)。但是我認(rèn)為客戶端也可以綁定。
4.3 網(wǎng)絡(luò)字節(jié)序和主機(jī)字節(jié)序
小端:小字節(jié)放前面,大字節(jié)放后面 大端:大字節(jié)放前面,小字節(jié)放后面 轉(zhuǎn)換端口 uint16_t htons(uint16_t hostshort); //主機(jī)字節(jié)序->網(wǎng)絡(luò)字節(jié)序 uint16_t ntohs(uint16_t netshort); //網(wǎng)絡(luò)字節(jié)序->主機(jī)字節(jié)序 轉(zhuǎn)IP htonl(uint32_t hostlong);//主機(jī)字節(jié)序->網(wǎng)絡(luò)字節(jié)序 ntohl(uint32_t netlong);//網(wǎng)絡(luò)字節(jié)序->主機(jī)字節(jié)序
4.4 listen監(jiān)聽(tīng)fd
int listen(fd,size) // 無(wú)錯(cuò)返回0,錯(cuò)誤返回-1
服務(wù)端調(diào)用listen()后,開(kāi)始監(jiān)聽(tīng)網(wǎng)絡(luò)上發(fā)送給socket的連接請(qǐng)求。
也就是說(shuō),開(kāi)始接收請(qǐng)求了。
4.5 connect發(fā)起連接請(qǐng)求
int connect(int sockfd, const struct sockaddr *serv_addr, int socklen_t addrlen);
4.6 accept()接收請(qǐng)求建立連接
accept()函數(shù)只做兩件事,將連接請(qǐng)求從全連接隊(duì)列中取出,給該連接分配一個(gè)fd并返回。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
4.7 消息的發(fā)送和接收
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); // 目的fd 消息 消息長(zhǎng)度 ssize_t write(int fd, const void *buf, size_t count); #include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
4.8 粘包問(wèn)題
2個(gè)數(shù)據(jù)包同時(shí)被提出,但是由于數(shù)據(jù)是在一起的,沒(méi)有辦法分離
解決方法:
- 在包頭添加一個(gè)數(shù)據(jù)包長(zhǎng)度的字段,標(biāo)明長(zhǎng)度來(lái)確定數(shù)據(jù)包
- 在包結(jié)束后添加分割符,這里注意分割符要選擇不經(jīng)常使用的
注意: 1優(yōu)于2,因?yàn)椋砑臃指罘?,需要遍歷整個(gè)消息來(lái)找到分隔符,這樣大大影響效率,但是2可以結(jié)合1使用。
4.9 close
#include <unistd.h> int close(int fd);
close操作只是使相應(yīng)socket描述字的引用計(jì)數(shù)-1,只有當(dāng)引用計(jì)數(shù)為0的時(shí)候,才會(huì)觸發(fā)TCP客戶端向服務(wù)器發(fā)送終止連接請(qǐng)求。
過(guò)程分析
1.正常情況下一方調(diào)用close情況如下圖:
2.當(dāng)雙方同時(shí)調(diào)用close,如下圖:
當(dāng)同時(shí)發(fā)送close時(shí),兩邊同時(shí)發(fā)送fin 和ack 這時(shí)候調(diào)用time_wait等待消息發(fā)送完畢。
Fin_wait_1作用?
等待對(duì)方回復(fù),超時(shí)自動(dòng)重發(fā)fin。
Fin_wait_2作用?
等待對(duì)方業(yè)務(wù)邏輯處理后,發(fā)送fin包。這里有可能出現(xiàn)死等待的情況服務(wù)器如果出現(xiàn)大量的Fin_wait_2可能需要考慮是不是沒(méi)有close,或者close之前做了耗時(shí)操作。time_wait 作用?
防止最后一個(gè)ACK沒(méi)有順利到達(dá)對(duì)方,超時(shí)重新發(fā)送ack。time_wait時(shí)常一般是120s可以修改。
服務(wù)器掉線重啟出現(xiàn)端口被占用怎么辦?
其實(shí)主要是由于還處于time_wait狀態(tài),端口并沒(méi)有真正釋放。這時(shí)候可以設(shè)置SO_REUSEADDR屬性,保證掉線能馬上重連。
到此這篇關(guān)于C++ POSIXAPI超詳細(xì)分析的文章就介紹到這了,更多相關(guān)C++ POSIXAPI內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)圖書(shū)管理系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)圖書(shū)管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07C++標(biāo)準(zhǔn)模板庫(kù)string類的介紹與使用講解
今天小編就為大家分享一篇關(guān)于C++標(biāo)準(zhǔn)模板庫(kù)string類的介紹與使用講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12C++數(shù)組模擬之單鏈表與雙鏈表和棧和隊(duì)列的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了C++數(shù)組模擬之單鏈表與雙鏈表和棧和隊(duì)列的實(shí)現(xiàn)過(guò)程,了解內(nèi)部原理是為了幫助我們做擴(kuò)展,同時(shí)也是驗(yàn)證了一個(gè)人的學(xué)習(xí)能力,如果你想讓自己的職業(yè)道路更上一層樓,這些底層的東西你是必須要會(huì)的,跟隨下文來(lái)具體了解吧2023-02-02