inet_pton和inetntop函數。字母p和n表明presentation和numeric。地址的表達presentation格式一般是ASCIL串,數值(numeric)格式則是存在於套接字地址結構中的二進制值。html
inet_pton和inet_ntop函數是比較新的函數,它們可以處理ipv4和ipv6的地址轉換。c#
1. inet_pton緩存
int inet_pton(int af, const char *src, void *dst);將src所指的網絡地址字符串(如"192.168.0.1")轉換成網絡使用的二進制數(unsigned long),存放在dst所指的in_addr結構中。使用基本與inet_aton一致,不一樣的是多了一個參數af(地址族:AF_INET或AF_INET6,分別對應ipv4和ipv6,對應的地址結構爲sockaddr_in和sockaddr_in6)。
使用:服務器
sockaddr_in server_addr; inet_pton(AF_INET, "192.168.0.1", (void *)&server_addr.sin_addr);
2. inet_ntop網絡
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);將src所指的網絡二進制數轉換成網絡地址字符串(如"192.168.0.1"),存放在dst所指字符串和返回值中。相比inet_pton多了個參數cnt,定義緩存區dst的大小,防止溢出。若是緩存區過小沒法存儲地址的值,則返回一個空指針,並將errno置爲ENOSPC。使用:socket
sockaddr_in server_addr; char IPdotdec[20]; //存放點分十進制IP地址 inet_pton(AF_INET, "192.168.0.1", (void *)&server_addr.sin_addr); printf(inet_ntop(AF_INET, (void *)&server_addr.sin_addr, IPdotdec, 16)); // 輸出"192.168.0.1" printf("%s", IPdotdec); // 輸出"192.168.0.1"
//從一個描述符讀取n個字節 ssize_t readn(int fd, void* vptr, size_t n) { size_t nleft = n; //記錄還剩下多少字節數沒讀取 ssize_t nread; //記錄已經讀取的字節數 char* ptr = vptr; //指向要讀取數據的指針 while(nleft > 0) //還有數據要讀取 { if(nread = read(fd,ptr,nleft) < 0) if(erron == EINTR)//系統被一個捕獲的信號中斷 nread = 0; //再次讀取 else return -1; //返回 else if(nread == 0)//沒有出錯可是也沒有讀取到數據 break; //再次讀取 nleft -= nread; //計算剩下未讀取的字節數 ptr += nread; //移動指針到以讀取數據的下一個位置 } return (n-nleft); //返回讀取的字節數 } /**************************************************************************************************/ //從一個描述符讀文本行,一次一個字節 ssize_t readline(int fd, void* vptr, size_t maxlen)//一個字節一個字節地讀取 { ssize_t rc; //每次讀取的字符 ssize_t n; //讀取的次數也即讀取字符串的長度 char c; // char* ptr = vptr; //指向要讀取的數據的指針 for(n = 1;n < maxlen; n++) { again: if((rc = read(fd,&c,1)) == 1) { *ptr++ = c; //移動指針 if(c == '\n') //換行符 break; //跳出循環 else if(rc == 0) //結束 *ptr = 0; //字符串以0結尾 return (n-1); //返回讀取的字節數 末尾的0不算 } else { if(erron == EINTR) goto again; //從新讀取 return (-1) } } *ptr=0; return n; } /**************************************************************************************************/ //往一個描述符寫n個字節 ssize_t writen(ind fd, const void* vptr, size_t n) { size_t nleft = n; //還須要寫入的字節數 ssize_t nwritten; //每次寫入的字節數 const char* ptr = vptr; //指向要寫入的數據的指針 while(nleft > 0) { if((nwritten = write(fd,ptr,nleft)) <= 0) { if(nwritten < 0 && erron == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; //計算還須要寫入的字節數 ptr += nwritten; //移動數據指針 } return n; }
值-結果參數
一個套接字函數傳遞一個套接字地址結構時候,該結構總以引用形式來傳遞,也就是說傳遞的指向該結構的一個指針,該結構的長度也做爲一個參數來傳遞,不過其傳遞方式取決於該結構的傳遞方向:是從進程到內核,仍是從內核到進程.以下圖所示:函數
fork和exec函數:spa
fork最困難的部分是它調用一次卻返回兩次。在調用進程(稱爲父進程),它返回一次,返回值是新派生進程(稱爲子進程)的進程ID,在子進程中它還返回一次,返回0。可經過返回值來判斷當前進程是子進程仍是父進程。.net
fork在子進程返回0而不是父進程ID,緣由是:子進程只有一個父進程,他總能夠調用getppid來獲得;而父進程有許多子進程,他沒有辦法獲得各子進程ID。若是父進程想跟蹤全部子進程ID,他必須記住fork的返回值。命令行
getsockname與getpeername
是返回套接口關聯的本地協議地址和遠程協議地址。
int getsockname(int sockfd, struct sockaddr * localaddr, socken_t * addrlen);
int getpeername(int sockfd, struct sockaddr * peeraddr, socken_t * addrlen);
返回0表示成功,返回1表示出錯
參數sockfd表示你要獲取的套接口的描述字。
localaddr返回本地協議地址描述結構, peeraddr返回遠程協議地址描述結構,addrlen分別是上述2個結構的長度。
注意,2個函數的最後一個參數是值-結果參數。
須要這兩個函數的理由以下:
在一個沒有調用bind的TCP客戶上,connect成功返回後,getsockname用於返回由內核賦予該鏈接的本地IP地址和本地端口號。
在以端口號爲0調用bind(告知內核去選擇本地臨時端口號)後,getsockname用於返回由內核賦予的本地端口號。
在一個以通配IP地址調用bind的TCP服務器上,與某個客戶的鏈接一旦創建(accept成功返回),getsockname就能夠用於返回由內核賦予該鏈接的本地IP地址。在這樣的調用中,套接字描述符參數必須是已鏈接套接字的描述符,而不是監聽套接字的描述符。
當一個服務器是由調用過accept的某個進程經過調用exec執行程序時,它可以獲取客戶身份的惟一途徑即是調用getpeername。
顯然,最後一個例子中的Telnet服務器必須在啓動以後獲取connfd的值。獲取該值有兩個經常使用方法:
調用exec的進程能夠把這個描述符格式化成一個字符串,再把它做爲一個命令行參數傳遞給新程序。
約定在調用exec以前,老是把某個特定描述符置爲所接受的已鏈接套接字的描述符。
inetd採用的是第二種方法,它老是把描述符0、一、2置爲所接受的已鏈接套接字的描述符(即將已鏈接套接字描述符dup到描述符0、一、2,而後close原鏈接套接字)。
服務器的代碼:
#include "unp.h" int main(int argc, char ** argv) { int listenfd,connfd; struct sockaddr_in servaddr; pid_t pid; char temp[20]; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(10010); Bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for( ; ; ) { struct sockaddr_in local; connfd = Accept(listenfd, (SA *)NULL, NULL); if((pid = fork()) == 0) { Close(listenfd);struct sockaddr_in serv, guest; char serv_ip[20]; char guest_ip[20]; socklen_t serv_len = sizeof(serv); socklen_t guest_len = sizeof(guest); getsockname(connfd, (struct sockaddr *)&serv, &serv_len); getpeername(connfd, (struct sockaddr *)&guest, &guest_len); Inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip)); Inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip)); printf("host %s:%d guest %s:%dn", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port)); char buf[] = "hello world"; Write(connfd, buf, strlen(buf)); Close(connfd); exit(0); } Close(connfd); } }
客戶端的代碼:
#include "unp.h" #define DEST_IP "127.0.0.1" int main(int argc, char ** argv) { int sockfd, n; char buf[100]; char serv_ip[20], guest_ip[20]; struct sockaddr_in servaddr; sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(struct sockaddr_in)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(10010); Inet_pton(AF_INET, DEST_IP, &servaddr.sin_addr); Connect(sockfd, (SA *)&servaddr, sizeof(servaddr)); struct sockaddr_in serv, guest; socklen_t serv_len = sizeof(serv); socklen_t guest_len = sizeof(guest); getsockname(sockfd, (SA *)&guest, &guest_len); getpeername(sockfd, (SA *)&serv, &serv_len); Inet_ntop(AF_INET, &guest.sin_addr, guest_ip, sizeof(guest_ip)); Inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip)); printf("host %s:%d, guest %s:%dn", serv_ip, ntohs(serv.sin_port), guest_ip, ntohs(guest.sin_port)); n = Read(sockfd, buf, 100); buf[n] = '�'; printf("%sn", buf); Close(sockfd); exit(0); }
TCP
對於服務器來講,在bind之後就能夠調用getsockname來獲取本地地址和端口,雖然這沒有什麼太多的意義。getpeername只有在連接創建之後才調用,不然不能正確得到對方地址和端口,因此他的參數描述字通常是連接描述字而非監聽套接口描述字。
對於客戶端來講,在調用socket時候內核還不會分配IP和端口,此時調用getsockname不會得到正確的端口和地址(固然連接沒創建更不可能調用getpeername),固然若是調用了bind 之後可使用getsockname。想要正確的到對方地址(通常客戶端不須要這個功能),則必須在連接創建之後,一樣連接創建之後,此時客戶端地址和端口就已經被指定,此時是調用getsockname的時機。
http://blog.chinaunix.net/zt/1016/unixjian_1016285.shtml