套接字(Socket)按照操作類型可以分為三類: 1.流式套接字(SOCK_STREAM):面向連接的Socket,只能收發(fā) TCP 協(xié)議的數(shù)據(jù); 2.數(shù)據(jù)報式套接字(SOCK_DGRAM):面向無連接的Socket,只能收發(fā) UDP 協(xié)議的數(shù)據(jù); 3.原始套接字(SOCK_RAW)可以用來自行組裝數(shù)據(jù)包,可以接收本機(jī)網(wǎng)卡上所有的數(shù)據(jù)幀(數(shù)據(jù)包),直接操作系統(tǒng)網(wǎng)絡(luò)核心(Network Core)。 Socket基本數(shù)據(jù)結(jié)構(gòu) 1.套接字地址結(jié)構(gòu) IPV4 A.新式機(jī)構(gòu) <netinet/in.h> struct sockaddr_in { unsigned short sin_len; //IPv4地址長度 short int sin_family; //指代協(xié)議簇,在TCP套接字編程只能是AF_INET unsigned short sin_port; //存儲端口號(使用網(wǎng)絡(luò)字節(jié)順序),數(shù)據(jù)類型是一個16為的無符號整形類型 struct in_addr sin_addr;//存儲IP地址,IP地址是一個in_add結(jié)構(gòu)體(結(jié)構(gòu)在下面) unsigned char sin_zero[8]; //為了讓sockaddr與sockaddr_in兩個數(shù)據(jù)結(jié)構(gòu)保持大小相同而保留的空字節(jié) }; struct in_addr { unsigned long s_addr; //按照網(wǎng)絡(luò)字節(jié)順序存儲IP地址 }; 大端模式和小端模式 1)小端就是低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端。 2)大端就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端。 舉一個例子,比如數(shù)字0x12 34 56 78在內(nèi)存中的表示形式為: A. 大端模式: 低地址 -----------------> 高地址 0x12 | 0x34 | 0x56 | 0x78 B. 小端模式: 低地址 ------------------> 高地址 0x78 | 0x56 | 0x34 | 0x12 B.老式結(jié)構(gòu) struct sockaddr { unsigned short sa_family; //套接字的協(xié)議簇地址類型,TCP/IP協(xié)議對于IPv4地址類型為AF_INET char sa_data[14];//存儲具體的協(xié)議地址 }; IPV6 A.新式結(jié)構(gòu) <netinet/in.h> struct sockaddr_in6 { unsigned short int sin6_len; //IPv6結(jié)構(gòu)長度,是一個無符號的8為整數(shù),表示128為IPv6地址長度 short int sin6_family; //地址類型AF_INET6 unsigned short int sin6_port; //存儲端口號,按網(wǎng)絡(luò)字節(jié)順序 unsigned short int sin6_flowinfo; //低24位是流量標(biāo)號,然后是4位的優(yōu)先級標(biāo)志,剩下四位保留 struct in6_addr sin6_addr; //IPv6地址,網(wǎng)絡(luò)字節(jié)順序 }; B.老式結(jié)構(gòu) struct in6_addr { unsigned long s6_addr; //128位的IPv6地址,網(wǎng)絡(luò)字節(jié)順序 }; 常用函數(shù) 1.socket函數(shù)? 函數(shù):int socket(int, int, int); ?功能:創(chuàng)建一個套接字 ?解釋:int socket(int af, int type, int protocol);
socket函數(shù)的family常值 socket函數(shù)的type常值 socket函數(shù)的protocol常值 socket函數(shù)中family和type參數(shù)的組合 2.bind函數(shù)? 函數(shù):int bind(int, const struct sockaddr *, socklen_t)? 功能:將套接字綁定到指定端口和地址上? 解釋: int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);
3.listen函數(shù) ?函數(shù):int listen(int, int)? 功能:將套接字設(shè)置為監(jiān)聽模式,對于流式套接字,必須處入監(jiān)聽模式才能夠接收客戶端套接字的連接 ?解釋:int listen(int sockfd, int backlog);
4.accept函數(shù)? 函數(shù):int accept(int, struct sockaddr * __restrict, socklen_t * __restrict)? 功能:接受客戶端的連接,在流式套接字中,只有在套接字處入監(jiān)聽狀態(tài),才能接受客戶端的連接? 解釋:int accept(int sockfd, struct sockaddr *addr, int *addrlen);
5.close函數(shù)?函數(shù):int close(int)? 功能:關(guān)閉套接字 ?解釋:int close(int sockfd); 6.connect函數(shù)? 函數(shù):int connect(int, const struct sockaddr *, socklen_t)? 功能:發(fā)送一個連接請求 解釋:int connect(int sockfd,const struct sockaddr * addr_server, int addrlen)
connect函數(shù)將激發(fā)TCP的三次握手過程,而且僅在連接建立成功或出錯時才返回,其中出錯有如下幾種情況: 1).若TCP客戶沒有收到SYN包的響應(yīng),則返回ETIMEDOUT錯誤。如調(diào)用該函數(shù)時,內(nèi)核發(fā)送一個SYN,若無響應(yīng)則等待6s后再發(fā)一個,若仍無響應(yīng),則等待24s再發(fā)一個,若總共等了75s后仍未收到響應(yīng)消息則返回該錯誤(因內(nèi)核而異)。 2).若響應(yīng)時RST,表明該服務(wù)器主機(jī)在我們指定的端口上沒有進(jìn)程等待,客戶收到RST包后馬上返回ECONNREFUSED錯誤。 3).若客戶發(fā)出的SYN在中間的路由器上引發(fā)了一個“destination unreachable”的ICMP錯誤,則按第一種情形繼續(xù)發(fā)送SYN,若在規(guī)定的時間內(nèi)沒有收到回應(yīng),則將ICMP錯誤作為EHOSTUNREACH或ENETUNREACH錯誤返回。 7.recv函數(shù) 函數(shù):ssize_t recv(int, void *, size_t, int) 功能:從流式套接字中接受數(shù)據(jù),平時開發(fā)針對TCP套接字接收數(shù)據(jù) 解釋:ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags)
成功時返回接收的字節(jié)數(shù)(收到EOF是返回0),失敗是返回-1 8.send函數(shù) 函數(shù):ssize_t send(int, const void *, size_t, int) 功能:在流式套接字中發(fā)送數(shù)據(jù) 解釋:ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags //表示函數(shù)的調(diào)用方式,一般填0,具體看下面表格 )
返回值:成功返回發(fā)送的字節(jié)數(shù),錯誤返回-1 9.recvfrom函數(shù) 函數(shù):ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict) 功能:用于接收一個數(shù)據(jù)包,并保存源地址 解釋: ssize_t recvfrom(int sockfd, void * buff, size_t nbytes, int flags, struct sockaddr * __restrict from, socklen_t * __restrict fromLen)
返回值:如果正確接收返回接收到的字節(jié)數(shù),失敗返回-1. 10.sendto函數(shù) 函數(shù):ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t) 功能:向指定的目的地方發(fā)送數(shù)據(jù) 解釋:ssize_t sendto(int sockfd, const void * buff, size_t nbytes, int flags, const struct sockaddr * to, socklen_t tolen)
成功則返回實際傳送出去的字符數(shù),失敗返回-1,錯誤原因存于errno 中 輔助函數(shù) 1.htons() 函數(shù) 和 ntohs()函數(shù)? 函數(shù):u_short htons(u_short hostshort)?功能:將一個16位的無符號端整形數(shù)據(jù)由主機(jī)排列方式轉(zhuǎn)換成網(wǎng)絡(luò)排列方式,所謂的網(wǎng)絡(luò)排列方式就是大端排列方式,MacOS是采用小端的存儲方式存儲數(shù)據(jù)? 使用地方:在有關(guān)主機(jī)地址和端口號結(jié)構(gòu)體中struct sockaddr_in里面,屬性in_port_t sin_port表示端口號,因為端口號要用網(wǎng)絡(luò)排列方式,使用該函數(shù)轉(zhuǎn)換后賦值 函數(shù): u_short ntohs(u_short netshort)?功能: 與htons()功能相反,將16位無符網(wǎng)絡(luò)排列端口轉(zhuǎn)換成主機(jī)排列方式,也就是將16位大端排列數(shù)字轉(zhuǎn)換成小端排列方式? 使用地方: 得到地址結(jié)構(gòu)體struct sockaddr_in,將里面的in_port_t sin_port轉(zhuǎn)換成我們平時看到的小端排列的端口號 2.htonl() 函數(shù) 和 ntohl()函數(shù) 函數(shù):u_long htonl(u_long hostlong) 功能:將一個32位無符號整形由主機(jī)排列方式轉(zhuǎn)換成網(wǎng)絡(luò)排列方式,所謂的網(wǎng)絡(luò)排列方式就是大端排列方式 使用地方:在有關(guān)主機(jī)地址和端口號結(jié)構(gòu)體中struct sockaddr_in里面,結(jié)構(gòu)體屬性struct in_addr sin_addr中的in_addr_t s_addr屬性表示IP地址信息,因為IP地址信息要用網(wǎng)絡(luò)排列方式,使用該函數(shù)轉(zhuǎn)換后賦值 函數(shù):u_long ntohl(u_long netlong) 功能:與函數(shù) htonl()功能相反,將網(wǎng)絡(luò)排列的32位無符數(shù)據(jù)轉(zhuǎn)換成主機(jī)排列,就是將32位大端排列數(shù)字轉(zhuǎn)換成小段排列數(shù)據(jù) 使用地方:得到地址結(jié)構(gòu)體struct sockaddr_in,將里面的in_addr_t s_addr轉(zhuǎn)換成我們平時看到的小端排列32位IP地址 3.inet_addr 函數(shù) 函數(shù):in_addr_t inet_addr(const char *) 功能:將存儲IP地址的char字符串轉(zhuǎn)換成網(wǎng)絡(luò)排列方式的32位無符號整形,跟上面htonl()函數(shù)功能一樣 使用地方:轉(zhuǎn)換的結(jié)果可直接用來給地址信息結(jié)構(gòu)體里面的IP地址賦值,因為轉(zhuǎn)換出來的結(jié)果是網(wǎng)絡(luò)排列的 4.inet_aton 函數(shù) 函數(shù):int inet_aton(const char *, struct in_addr *); 功能:與函數(shù)inet_addr功能一樣,將char字符串IP地址轉(zhuǎn)換成網(wǎng)絡(luò)排序的無符整形,傳入struct in_addr結(jié)構(gòu)體指針,直接賦給結(jié)構(gòu)體 5.inet_pton 函數(shù) 函數(shù):int inet_pton(int, const char *, void *); 功能:與輔助函數(shù)htonl()、inet_addr()、inet_aton()的功能一樣,將char字符串IP地址轉(zhuǎn)換成網(wǎng)絡(luò)排序的無符整形,直接賦給struct in_addr結(jié)構(gòu)體指針里面,不一樣的是可以根據(jù)地址族的不同轉(zhuǎn)換IPv6還是IPv4的地址 6.inet_ntoa 函數(shù) 函數(shù):char *inet_ntoa(struct in_addr) 功能:正好與上面的函數(shù)inet_aton功能相反,需要傳入一個關(guān)于地址信息的結(jié)構(gòu)體,解析出來C字符串的IP地址 7.inet_ntop 函數(shù) 函數(shù):const char *inet_ntop(int, const void *, char *, socklen_t); 功能:跟上面函數(shù)inet_ntoa()功能相似,于函數(shù)inet_pton()功能相反,不一樣的是他可以傳入地址族,傳入AF_INET則解析出IPv4的地址,傳入AF_INET6則解析 8.getpeername 函數(shù)? 函數(shù):int getpeername(int, struct sockaddr * __restrict, socklen_t * __restrict)?功能:獲取socket套接字對方的地址信息,返回0時正常,否則錯誤? 解釋:int getpeername(int sockfd, struct sockaddr * peerAddr, socklen_t * addrLen)
8.getsockname 函數(shù)? 函數(shù):int getsockname(int, struct sockaddr * __restrict, socklen_t * __restrict)?功能:獲取socket套接字的地址信息,返回0時正常,否則錯誤? 解釋:int getsockname(int sockfd, struct sockaddr * addr, socklen_t *addrLen)
|
|