#include <netinet/in.h> struct sockaddr { unsigned short sa_family; /*地址族*/ char sa_data[14]; /*14字節的協議地址,包含該socket的IP地址和端口號。*/ }; struct sockaddr_in { short int sa_family; /*地址族*/ unsigned short int sin_port; /*端口號*/ struct in_addr sin_addr; /*IP地址*/ unsigned char sin_zero[8]; /*填充0 以保持與struct sockaddr同樣大小*/ }; struct in_addr { unsigned long int s_addr; /* 32位IPv4地址,網絡字節序 */ }; #include <netinet/in.h> tips
sa_family: AF_INET -> IPv4協議 AF_INET6 -> IPv6協議
結構體struct in_addr中存放的s_addr,是無符號整型數。實際上32位IPv4地址為點分十進制,每個字節的范圍均為0-255,只要高字節大於等於128,那麼這個整型數必然為負數,只不過我們這邊僅僅關心ip每一位的存儲情況,因此此處可以使用無符號數進行存儲。
SYNOPSIS #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp);/* 注意,參數inp為傳出參數 */ char *inet_ntoa(struct in_addr in);
實際上,我們在上篇文章中實現的三個函數是有系統函數可以直接調用的。我們的my_atoh,my_hton合並為系統函數inet_aton,而my_ntoa即為系統函數inet_ntoa。
/************************************************************************* > File Name: test.c > Author: KrisChou > Mail:[email protected] > Created Time: Wed 27 Aug 2014 11:06:11 PM CST ************************************************************************/ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { char ip_buf[] = "180.97.33.107"; struct in_addr my_addr; inet_aton(ip_buf,&my_addr); printf("ip : %s \n", ip_buf); printf("net: %x \n", my_addr.s_addr); return 0; }
運行結果
[purple@localhost 0827]$ gcc -o test test.c -Wall [purple@localhost 0827]$ ./test ip : 180.97.33.107 net: 6b2161b4
照理,網絡字節序是大端存儲,應該返回0xb461216b。實際上調用系統函數inet_aton後,就直接在變量my_addr.s_addr的實際內存空間中以二進制形式寫入了0xb461216b(其實用位運算,就可以直接操作二進制位,上篇博文有具體實現)。之所以運行結果是0x6b2161b4,是因為我們的主機是小端存儲,用printf顯示結果是先取低字節。
/************************************************************************* > File Name: test1.c > Author: KrisChou > Mail:[email protected] > Created Time: Wed 27 Aug 2014 11:43:26 PM CST ************************************************************************/ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { struct in_addr my_addr; my_addr.s_addr = 0xb461216b; printf("net: %x \n", my_addr.s_addr); printf("ip : %s \n", inet_ntoa(my_addr)); return 0; }
運行結果
[purple@localhost 0827]$ gcc -o test1 test1.c -Wall [purple@localhost 0827]$ ./test1 net: b461216b ip : 107.33.97.180
照理,ip應該輸出的是180.97.33.107。其實道理很簡單,我們的主機是小端模式存儲,而網絡字節序是大端模式,當我們把0xb461216b賦值給my_addr.s_addr 時,實際上在內存中存儲形式是0x6b2161b4,而inet_ntoa的具體實現時通過位運算直接操縱二進制位的,因此結果就自然輸出107.33.97.180。
SYNOPSIS #include <netdb.h> struct hostent *gethostbyname(const char *name); The hostent structure is defined in <netdb.h> as follows: struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] /* for backward compatibility */ The members of the hostent structure are: h_name The official name of the host. h_aliases An array of alternative names for the host, terminated by a NULL pointer. h_addrtype The type of address; always AF_INET or AF_INET6 at present. h_length The length of the address in bytes. h_addr_list An array of pointers to network addresses for the host (in net- work byte order), terminated by a NULL pointer. h_addr The first address in h_addr_list for backward compatibility.
/************************************************************************* > File Name: my_host.c > Author: KrisChou > Mail:[email protected] > Created Time: Wed 27 Aug 2014 05:22:46 PM CST ************************************************************************/ #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> int main(int argc, char* argv[])// exe hostname { struct hostent* p ; p = gethostbyname(argv[1]) ; /* struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; } #define h_addr h_addr_list[0] */ printf("host name: %s \n", p ->h_name); int index ; char** pp = p -> h_aliases ; for(index = 0 ; pp[index] != NULL; index ++ ) { printf("alias : %s \n", pp[index]); } printf("ip type : %d\n", p ->h_addrtype); printf("addr len : %d \n", p ->h_length); pp = p ->h_addr_list ; for(index = 0; pp[index] != NULL ; index ++) { /* 由於h_addr_list是一個字符串指針數組,數組中存放的指針指向一個網絡字節序 但是系統函數inet_ntoa需要傳入的參數是一個結構體,因此需要進行轉換。 pp[index]是一個char*類型的指針,先將其轉換為struct in_addr*類型的指針, 接著去引用,即得到結構體。 */ printf("ip : %s \n", inet_ntoa( *(struct in_addr *)pp[index] ) ); } return 0 ; }
運行結果
[purple@localhost 0827]$ gcc -o myhost my_host.c -Wall [purple@localhost 0827]$ ./myhost www.baidu.com host name: www.a.shifen.com alias : www.baidu.com ip type : 2 addr len : 4 ip : 180.97.33.107 ip : 180.97.33.108
某年騰訊面試題:
#include <stdio.h> #include <stdlib.h> int main() { int a = 0x61;//97 printf("%x\n",(char*)(&a)[0]); }
切記系統函數無論inet_aton還是inet_ntoa,都是直接以位運算形式實現的,因此其關注的是數據在內存中實際的二進制存儲形式。
意思你想看源代碼?一般都是以標准庫+頭文件的形式。。。。
看源碼的話,就去下載glibc的源碼包,裡面包含了gnu的c函數庫的源碼。
這方面的書似乎沒見過,一般都不會去分析標准庫,而是分析內核源碼。
這個鏈接是介紹glibc的
hi.baidu.com/...9.html
這個連接是下載glibc的:(自己仔細看下,是英文的)
www.gnu.org/s/libc/resources.html
書的話,很抱歉就看到這個:程序員的自我修養:鏈接、裝載與庫(似乎是說windows的)
不過我搜索的時候看到這裡面的11章說到了一些,具體我沒有看~~~
剩下的就看你自己咯~~
linux網絡編程的I/O 多路復用。select()函數是系統提供的,它可以在多個描
述符中選擇被激活的描述符進行操作。
例如:一個進程中有多個客戶連接,即存在多個TCP 套接字描述符。select()函數阻塞
直到任何一個描述符被激活,即有數據傳輸。從而避免了進程為等待一個已連接上的數據而
無法處理其他連接。因而,這是一個時分復用的方法,從用戶角度而言,它實現了一個進程
或線程中的並發處理。
I/O 多路復用技術的最大優勢是系統開銷小,系統不必創建進程、線程,也不必維護這
些進程/線程,從而大大減少了系統的開銷。
select()函數用於實現I/O 多路復用,它允許進程指示系統內核等待多個事件中的任何一
個發生,並僅在一個或多個事情發送或經過某指定的時間後才喚醒進程。
它的原型如下,
#include<sys/time.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set * errorfds, struct timeval *timeout);
ndfs: select() 函數監視描述符數的最大值。根據進程中打開的描述符數而定,一般設為要
監視的描述符的最大數加1。
readfds: select() 函數監視的可讀描述符集合。
writefds: select()函數監視的可寫描述符集合。
errorfds: select()函數監視的異常描述符集合。
timeout: select()函數超時結束時間
返回值。如果成功返回總的位數,這些位對應已准備好的描述符。否則返回-1,並在errno
中設置相應的錯誤碼。
FD_ZERO(fd_set *fdset):清空fdset 與所有描述符的聯系
FD_SET(int fd, fd_set *fdset):建立描述符fd 與fdset 的聯系
FD_CLR(int fd, fd_set *fdset):撤銷描述符fd 與fdset 的聯系
FD_ISSET(int fd,fd_set *fdset) ::檢查與fdset 聯系的描述符fd 是否可讀寫,返回非0表示可讀寫。
采用select()函數實現I/O 多路復用的基本步驟如下:
(1) 清空描述符集合
(2) 建立需要監視的描述符與描述符集合的聯系
(3) 調用select()函數
(4) 檢查所有需要監視的描述符,利用FD_ISSET 判斷是否准備好
(5) 對已准備好的描述符進行I/O 操作