第3章 套接字編程簡介
1.套接字的基本結構
數據定義:
struct sockaddr {
unsigned short sa_family; /* address族, AF_xxx */
char sa_data[14]; /* 14 bytes的協議地址 */
};
sa_family 一般來說,都是“AFINET”。
sa_data 包含了一些遠程電腦的地址、端口和套接字的數目,它裏面的數據是雜溶在一
切的。
爲了處理struct sockaddr, 程序員建立了另外一個相似的結構 struct sockaddr_in:
struct sockaddr_in (“in” 代表 “Internet”)
struct sockaddr_in {
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口號 */
struct in_addr sin_addr; /* Internet地址 */
unsigned char sin_zero[8]; /* 添0(和struct sockaddr一樣大小)*/
};
這個結構提供了方便的手段來訪問socket address(struct sockaddr)結構中的每一個元
素
2.套接字字節轉換程序的列表:計算機內存中有兩種數據存儲方式,一種爲小端字節序,也就是低地址存儲數據低字節,高地址存儲數據高字節;一種爲大端字節序,也就是低地址存儲高字節,高地址存儲低字節。網絡字節序採用大端字節序,而主機字節序有可能爲小端字節序,因此,存在着字節序的轉換問題。
l htons()——“Host to Network Short”主機字節順序轉換爲網絡字節順序(對無符號
短型進行操作4 bytes)
l htonl()——“Host to Network Long” 主機字節順序轉換爲網絡字節順序(對無符
號長型進行操作8 bytes)
l ntohs()——“Network to Host Short “ 網絡字節順序轉換爲主機字節順序(對無符
號短型進行操作4 bytes)
l ntohl()——“Network to Host Long “ 網絡字節順序轉換爲主機字節順序(對無符
號長型進行操作8 bytes)
3. IP 地址轉換
in_addr_t inet_addr(const char *straddr);
int inet_aton(const char* straddr,struct in_addr *addrp);
char* inet_ntoa(struct in_addr inaddr);
頭文件:sys/socket.h netinet/in.h arpa/inet.h
inet_addr成功返回32位網絡字節序地址,出錯返回INADDR_NONE。INADDR_NONE爲linux定義的一個常數,是一個不存在的ip地址;
inet_aton將ASCII轉換成網絡字節序的32位二進制值,輸入的ASCII放在straddr中,轉換後放在addrp中,成功返回1,失敗返回0;
inet_ntoa將32位二進制地址轉換成ASCII地址,成功返回ASCII值,失敗返回NULL。
Linux 系統提供和很多用於轉換IP 地址的函數.首先,假設你有一個struct sockaddr_in ina,並且你的IP 是166.111.69.52 ,你想把你的IP 存儲到ina 中。你可以使用的函數: inet_addr() ,它能夠把一個用數字和點表
示IP 地址的字符串轉換成一個無符號長整型。你可以像下面這樣使用它:
ina.sin_addr.s_addr = inet_addr(“166.111.69.52”);
注意:
l inet_addr() 返回的地址已經是網絡字節順序了,你沒有必要再去調用htonl() 函數
反過來,如果你有一個struct in_addr 並且你想把它代表的IP 地址打印出來(按照數字.數字.數字.數字的格式),那麼你可以使用函數inet_ntoa()(“ntoa”代表“Network to ASCII”),它會把struct in_addr 裏面存儲的網絡地址以數字.數字.數字.數字的格式。
l inet_ntoa() 使用struct in_addr 作爲一個參數,不是一個長整型值。
4.字節操作函數
字節操作函數主要是用於讀取結構體中的某幾個字節。
void bzero(void *dest,size_t nbytes);
void bcopy(const void *src,void *dest,size_t nbytes);
int bcmp(const void *prt1,const void *ptr2,size_t nbytes);
void *memset(void *dest,int c,size_t len);
void *memcpy(void *dest,const void *src,size_t nbytes);
int memcmp(const void *ptr1,const void ptr2,size_t nbytes);
頭文件:string.h
說明:以b打頭的函數爲支持套接口函數的系統所提供,mem爲支持ANSI C庫提供的函數;其中,bzero將制定的起始地址設置爲0(nbytes表示字長),bcopy和memcpy爲複製,bcmp和memcmp爲比較,memset將目標中指定數據的字節設置爲指定的值。
5.readn,writen和readline函數
第4章 基於TCP套接字編程
基本TCP客戶-服務器程序的套接口函數如下圖:
1、socket函數
原型:#include<sys/socket.h>
int socket(int family, int type, int protocol); 返回值:非負描述符--成功, -1--出錯;
作用:指定期望的通信協議接口(TCP或UDP或unix域字節協議等)
family 指明協議簇
族 | 解釋 |
AF_INET | IPv4協議 |
AF_INET6 | IPv6協議 |
AF_LOCAL | Unix與協議 |
AF_ROUTE | 路由套接口 |
AF_KEY | 密鑰套接口 |
類型 | 解釋 |
SOCK_STREAM | 字節流套接口 |
SOCK_DGRAM | 數據包套接口 |
SOCK_RAW | 原始套接口 |
protocol參數應設置爲某個協議類型常值,或者設爲0.
protocol | 說明 |
IPPROTO_TCP | TCP傳輸協議 |
IPPROTO_UDP | UDP傳輸協議 |
IPPROTO_SCTP | SCTP傳輸協議 |
2、connect函數
原型:#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);返回值:0---成功,-1----出錯;
作用:客戶端用connect 函數建立一個與TCP服務器的連接;
參數:sockfd,套接口描述字;
servaddr,套接口地址結構的指針,該地址結構需要包含服務器IP地址和端口號;
addrlen, 套接口地址結構的大小;
關於connect出錯返回的幾種情況:- 如果TCP客戶端沒有收到SYN分節的響應,則返回ETIMEDOUT。
- 如果對客戶的SYN的響應是RST,則表明該服務器主機在我們指定的端口上沒有進程在等待與之連接,這稱之爲硬錯,客戶端接到RST,立即返回錯誤ECONNREFUSED;
- 如果某客戶端發出的SYN在中間的路由器上引發了一個目的地不可達的ICMP錯誤,這稱之爲軟錯,客戶端按時間間隔繼續發送SYN,在規定時間仍未收到響應,則返回EHOSTUNREACH;
原型:#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);返回值:0---成功,-1----出錯;
作用:給套接口分配一個本地協議地址;
4、listen函數
原型:#include<sys/socket.h>
int listen(int sockfd, int backlog); 返回值:0---成功,-1---出錯;
作用 : 將未連接的套接口轉換成被動套接口,指示內核應接受此套接口的連接請求;第二個參數規定了次套接口排隊的最大連接數;
參數:sockfd,套接口描述字;
backlog,套接口排隊的最大連接數;
對與第二個參數backlog的理解:
- 未完成連接隊列,爲每個已由客戶端發出併到達服務器,服務器正在等待完成相應TCP三路握手過程的SYN分節開設一個條目,這些套接口都處於SYN_RCVD;
- 已完成隊列:爲每個已完成TCP三路握手過程的客戶開設一個條目,這些套接口都處於ESTABLISHED狀態;
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);返回值:非負描述符---成功,-1----出錯;
作用:有服務器調用,從已完成連接隊列頭返回下一個已完成連接,若已完成連接隊列爲空,則進程睡眠。