#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>
tipssa_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:zhoujx0219@163.com
> 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:zhoujx0219@163.com
> 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:zhoujx0219@163.com
> 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 操作