C++ POSIX API超詳細分析
1.網絡通信
1.消息傳遞(管道、FIFO、消息隊列)
2.同步(互斥量、條件變量、讀寫鎖、文件和寫記錄鎖、信號量)
3.共享內存(匿名的和具名的)
使用TCP/IP協議 通過socket完成
2.posix API
目的:實現不同系統上的源代碼的可移植性。
舉例:linux和windows都要實現基本的posix標準,linux把fork函數封裝成posix_fork(隨便說的),windows把creatprocess函數也封裝成posix_fork,都聲明在unistd.h里。這樣,程序員編寫普通應用時候,只用包含unistd.h,調用
3.POSIX網絡API
4.函數內部過程解析
4.1 socket套接字創(chuàng)建
int socket(int domain, int type, int protocol); //參數分別是地址族、 套接字類型和協議 //AF_INET SOCK_STREAM 可默認為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:一個const struct sockaddr *指針,指向要綁定給sockfd的協議地址。
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)聽時,監(jiān)聽所有的地址 server_addr.sin_port = htons(port);
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET 協議族*/ in_port_t sin_port; /* port in network byte order 端口號*/ struct in_addr sin_addr; /* internet address IP地址*/ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
通常服務器在啟動的時候都會綁定一個眾所周知的地址(如ip地址+端口號),用于提供服務,客戶就可以通過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。這就是為什么通常服務器端在listen之前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。但是我認為客戶端也可以綁定。
4.3 網絡字節(jié)序和主機字節(jié)序
小端:小字節(jié)放前面,大字節(jié)放后面 大端:大字節(jié)放前面,小字節(jié)放后面 轉換端口 uint16_t htons(uint16_t hostshort); //主機字節(jié)序->網絡字節(jié)序 uint16_t ntohs(uint16_t netshort); //網絡字節(jié)序->主機字節(jié)序 轉IP htonl(uint32_t hostlong);//主機字節(jié)序->網絡字節(jié)序 ntohl(uint32_t netlong);//網絡字節(jié)序->主機字節(jié)序
4.4 listen監(jiān)聽fd
int listen(fd,size) // 無錯返回0,錯誤返回-1
服務端調用listen()后,開始監(jiān)聽網絡上發(fā)送給socket的連接請求。
也就是說,開始接收請求了。
4.5 connect發(fā)起連接請求
int connect(int sockfd, const struct sockaddr *serv_addr, int socklen_t addrlen);
4.6 accept()接收請求建立連接
accept()函數只做兩件事,將連接請求從全連接隊列中取出,給該連接分配一個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 消息 消息長度 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 粘包問題
2個數據包同時被提出,但是由于數據是在一起的,沒有辦法分離
解決方法:
- 在包頭添加一個數據包長度的字段,標明長度來確定數據包
- 在包結束后添加分割符,這里注意分割符要選擇不經常使用的
注意: 1優(yōu)于2,因為,添加分割符,需要遍歷整個消息來找到分隔符,這樣大大影響效率,但是2可以結合1使用。
4.9 close
#include <unistd.h> int close(int fd);
close操作只是使相應socket描述字的引用計數-1,只有當引用計數為0的時候,才會觸發(fā)TCP客戶端向服務器發(fā)送終止連接請求。
過程分析
1.正常情況下一方調用close情況如下圖:
2.當雙方同時調用close,如下圖:
當同時發(fā)送close時,兩邊同時發(fā)送fin 和ack 這時候調用time_wait等待消息發(fā)送完畢。
Fin_wait_1作用?
等待對方回復,超時自動重發(fā)fin。
Fin_wait_2作用?
等待對方業(yè)務邏輯處理后,發(fā)送fin包。這里有可能出現死等待的情況服務器如果出現大量的Fin_wait_2可能需要考慮是不是沒有close,或者close之前做了耗時操作。time_wait 作用?
防止最后一個ACK沒有順利到達對方,超時重新發(fā)送ack。time_wait時常一般是120s可以修改。
服務器掉線重啟出現端口被占用怎么辦?
其實主要是由于還處于time_wait狀態(tài),端口并沒有真正釋放。這時候可以設置SO_REUSEADDR屬性,保證掉線能馬上重連。
到此這篇關于C++ POSIXAPI超詳細分析的文章就介紹到這了,更多相關C++ POSIXAPI內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!