C语言 网络地址

来源:互联网 发布:离散数学 考研 知乎 编辑:程序博客网 时间:2024/06/05 07:28
网络编程中,或多或少都会与网络地址打交道,我们从易到难,再来一个三层境界,菜鸟级,入门级,进阶级。下面一一说明。菜鸟级:菜鸟都知道,网络编程中要用到IP地址,IP地址是一个32为的整数,是一个unsigned int或unsigned long, 我编程中见到的int与long都是4个自己的!将一个doted字符串IP地址转化为整型IP地址: unsigned long inet_addr( const char* cp ); 失败的化返回INADDR_NONE(0xffffffff) 竟然是一个广播地址,所以这个操作完成后,一定要检查返回值!整形IP地址其实就是下面的结构(这个结构被称为最恶心的结构),你可以直接转过去:typedef struct in_addr {union {struct {u_char s_b1,s_b2,s_b3,s_b4;} S_un_b;struct {u_short s_w1,s_w2;} S_un_w;u_long S_addr;} S_un;} in_addr;将一个整型IP地址转华为doted字符串IP地址: char* FAR inet_ntoa( struct in_addr in ); 你完全可以直接参数整数!这里要注意的仍然是返回值,这个值保存在被Socket实现内部的内存中,MSDN中说明Windows下,同一线程中下一次调用inet_ntoa将会破坏这个值,所以你可能需要保存这个返回值。这个内存也不需要释放。一种可能的实现如下:char FAR * inet_ntoa (struct in_addr in){static char strRet[16];// ...return strRet;}以后请不要对这个指针的使用有太多的疑惑!黄金规则: 所有被返回的地址都是网络地址(字节序从左到右)Linux下的表中C库中,IP地址的操作与转化可以使用下面的函数,不多说了,请大家man!#include <sys/types.h>   #include <sys/socket.h>   #include <netinet/in.h>   #include <arpa/inet.h>   int   inet_aton(const char *cp, struct in_addr *pin);   in_addr_t   inet_addr(const char *cp);   in_addr_t   inet_network(const char *cp);   char *   inet_ntoa(struct in_addr in);   const char *   inet_ntop(int af, const void * restrict src, char * restrict dst,       socklen_t size);   int   inet_pton(int af, const char * restrict src, void * restrict dst);   struct in_addr   inet_makeaddr(in_addr_t net, in_addr_t lna);   in_addr_t   inet_lnaof(struct in_addr in);   in_addr_t   inet_netof(struct in_addr in);   char *   inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size);   int   inet_net_pton(int af, const char *src, void *dst, size_t size);除了上面的两个函数外,Windows下不再有其他的inet_开头的兼容函数!诸位可能要嚷嚷了,我老早以前就不是菜鸟了,好的!让我们马上进入下一个阶段!入门级:入门级的两个代表是函数是 struct HOSTENT* FAR gethostbyaddr( const char* addr, int len, int type ); 与 struct hostent* FAR gethostbyname( const char* name );我们再来看另外一个重要的结构:typedef struct hostent {char FAR* h_name;char FAR FAR** h_aliases;short h_addrtype;short h_length;char FAR FAR** h_addr_list;} hostent;h_nameOfficial name of the host (PC). If using the DNS or similar resolution system, it is the Fully Qualified Domain Name (FQDN) that caused the server to return a reply. If using a local hosts file, it is the first entry after the IP address.h_aliasesNull-terminated array of alternate names.h_addrtypeType of address being returned.h_lengthLength of each address, in bytes.h_addr_listNull-terminated list of addresses for the host. Addresses are returned in network byte order. The macro h_addr is defined to be h_addr_list[0] for compatibility with older software.struct hostent* FAR gethostbyname( const char* name ) 这个函数的恶心之处在于在Windows下,输入的参数不能是doted字符串IP地址,只能时域名,要不然工作不正常.Linux没有这样的限制。struct HOSTENT* FAR gethostbyaddr( const char* addr, int len, int type ) 的恶心之处在于参数过于复杂且相互影响,各参数描述如下addr[in] Pointer to an address in network byte order. (一个描述地址的结构转化为字符串指针)len[in] Length of the address, in bytes. (地址结构的长度,不同的长度代表不同的地址类型)type[in] Type of the address, such as the AF_INET address family type (defined as TCP, UDP, and other associated Internet protocols). Address family types and their corresponding values are defined in the Winsock2.h header file. (地址组类型,到现在位置,我页不知道到底有多少地址类型)这三个恶心的参数导致了这个函数强大的扩展能力。整 形IP地址 unsigned long addr = inet_addr("192.168.1.39"); struct hostend* remoteHost = gethostbyaddr((char *) &addr, sizeof(unsigned long)(4), AF_INET);sockaddr型的地址: struct sockaddr addr; struct hostend* remoteHost = gethostbyaddr((char *) &addr, sizeof(struct sockaddr)(16), addr.sa_family);我们说道sockaddr结构了,他的定义如下:struct sockaddr {   unsigned short sa_family;   char sa_data[14];};这个结构也不太稳定,容易变异. 如果是internet地址组,也就是sa_family==AF_INET的情况下,这个结构变成 struct sockaddr_in{   short sin_family;   unsigned short sin_port;   struct in_addr sin_addr;   char sin_zero[8];};上面的结构里出现了结构in_addr, 怎么,忘了,到菜鸟级再去看看!另外sockaddr类的结构中包含了应用成信息,有个端口号! hostent 结构里的 short h_addrtype; short h_length; char FAR FAR** h_addr_list; 直接受到gethostbyaddr的三个参数的控制!下面还有几个我没有用过的,也不知道Linux下是否对应!struct sockaddr_in6 {short sin6_family;u_short sin6_port;u_long sin6_flowinfo;struct in6_addr sin6_addr;u_long sin6_scope_id;};struct sockaddr_in6_old {      short   sin6_family;            u_short sin6_port;              u_long  sin6_flowinfo;          struct  in6_addr sin6_addr;};struct in6_addr {union {  u_char Byte[16];  u_short Word[8];} u;};typedef struct _SOCKADDR_IRDA {u_short irdaAddressFamily;u_char irdaDeviceID[4];char irdaServiceName[25];} SOCKADDR_IRDA,typedef struct sockaddr_storage {short ss_family;char __ss_pad1[_SS_PAD1SIZE];__int64 __ss_align;char __ss_pad2[_SS_PAD2SIZE];} SOCKADDR_STORAGE,gethostbyname 和 gethostbyaddr在Windows下已经被deprecated,用getaddrinfo 代替!我们进入入第三个阶段来看看这个函数!进阶级:先来看看下面的函数与结构int getaddrinfo( const TCHAR* nodename, const TCHAR* servname, const struct addrinfo* hints, struct addrinfo** res );nodename[in] Pointer to a null-terminated string containing a host (node) name or a numeric host address string. The numeric host address string is a dotted-decimal IPv4 address or an IPv6 hex address.servname[in] Pointer to a null-terminated string containing either a service name or port number.hints[in] Pointer to an addrinfo structure that provides hints about the type of socket the caller supports. See Remarks.res[out] Pointer to a linked list of one or more addrinfo structures containing response information about the host.typedef struct addrinfo {int ai_flags;int ai_family;int ai_socktype;int ai_protocol;size_t ai_addrlen;TCHAR* ai_canonname;struct sockaddr* ai_addr;struct addrinfo* ai_next;} addrinfo;ai_flagsFlags that indicate options used in the getaddrinfo function. See AI_PASSIVE, AI_CANONNAME, and AI_NUMERICHOST.ai_familyProtocol family, such as PF_INET.ai_socktypeSocket type, such as SOCK_RAW, SOCK_STREAM, or SOCK_DGRAM.ai_protocolProtocol, such as IPPROTO_TCP or IPPROTO_UDP. For protocols other than IPv4 and IPv6, set ai_protocol to zero.ai_addrlenLength of the ai_addr member, in bytes.ai_canonnameCanonical name for the host.ai_addrPointer to a sockaddr structure.ai_nextPointer to the next structure in a linked list. This parameter is set to NULL in the last addrinfo structure of a linked list.int getnameinfo( const struct sockaddr* sa, socklen_t salen, TCHAR* host, DWORD hostlen, TCHAR* serv, DWORD servlen, int flags );sa[in] Pointer to a socket address structure containing the address and port number of the socket. For IPv4, the sa parameter points to a sockaddr_in structure; for IPv6, the sa parameter points to a sockaddr_in6 structure.salen[in] Length of the structure pointed to in the sa parameter, in bytes.host[out] Pointer to the host name. The host name is returned as a Fully Qualified Domain Name (FQDN) by default.hostlen[in] Length of the buffer pointed to by the host parameter, in bytes. The caller must provide a buffer large enough to hold the host name, including terminating NULL characters. A value of zero indicates the caller does not want to receive the string provided in host.serv[out] Pointer to the service name associated with the port number.servlen[in] Length of the buffer pointed to by the serv parameter, in bytes. The caller must provide a buffer large enough to hold the service name, including terminating null characters. A value of zero indicates the caller does not want to receive the string provided in serv.flags[in] Flags used to customize processing of the getnameinfo function. See Remarks.void freeaddrinfo( struct addrinfo* ai );ai[in] Pointer to the addrinfo structure or linked list of addrinfo structures to be freed. All dynamic storage pointed to within the addrinfo structure(s) is also freed.----------------------------------------------------------...//--------------------------------// Declare and initialize variables.char* ip = "127.0.0.1";char* port = "27015";struct addrinfo aiHints;struct addrinfo *aiList = NULL;int retVal;//--------------------------------// Setup the hints address info structure// which is passed to the getaddrinfo() functionmemset(&aiHints, 0, sizeof(aiHints));aiHints.ai_family = AF_INET;aiHints.ai_socktype = SOCK_STREAM;aiHints.ai_protocol = IPPROTO_TCP;//--------------------------------// Call getaddrinfo(). If the call succeeds,// the aiList variable will hold a linked list// of addrinfo structures containing response// information about the hostif ((retVal = getaddrinfo(ip, port, &aiHints, &aiList)) != 0) {printf("getaddrinfo() failed.n");}------------------------------------------------------------...//-----------------------------------------// Declare and initialize variablesstruct sockaddr_in saGNI;char hostName[256];char servInfo[256];u_short port;port = 27015;//-----------------------------------------// Set up sockaddr_in structure which is passed// to the getnameinfo functionsaGNI.sin_family = AF_INET;saGNI.sin_addr.s_addr = inet_addr(ip);saGNI.sin_port = htons(port);//-----------------------------------------// Call getnameinfoif ((retVal = getnameinfo((SOCKADDR *)&saGNI,sizeof(sockaddr),hostName,256,servInfo,256,NI_NUMERICSERV)) != 0) {printf("getnameinfo() failed.n");printf("Error #: %ldn", WSAGetLastError());}这几个函数就比较高级,与前面两级的的函数相比,已经上升到应用而不是停留在网络,而且用户自主管理内存,这样的好处是支持可重入的并发使用,到了这个阶段,也不需要多说了,手册就在那里,自己看了!下面在在第一二阶段的函数基础上对getaddrinfo的简单实现!作为对此文的总结!intfake_getaddrinfo(const char *hostname, const char* strport, const struct addrinfo* hints,struct addrinfo **ai){struct hostent *he;int addr = inet_addr(hostname);struct sockaddr_in *saddr;*ai = (struct addrinfo *)malloc(sizeof(struct addrinfo));/*The gethostbyname function cannot resolve IP address strings passed to it.Such a request is treated exactly as if an unknown host name were passed.Use inet_addr to convert an IP address string the string to an actual IP address,then use another function, gethostbyaddr, to obtain the contents of the hostent structure.*/if(addr==INADDR_NONE){he = gethostbyname(hostname);if (!he)return (-1);ai->ai_family = hints->ai_family;ai->ai_socktype = hints->ai_socktype; //SOCK_RAW, SOCK_STREAM, or SOCK_DGRAM.ai->ai_protocol = hints->ai_proocol; //IPPROTO_TCP or IPPROTO_UDP or 0ai->ai_canonname = hostname;ai->ai_addrlen = sizeof(struct sockaddr);if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen)))return (-1);saddr = (struct sockaddr_in*)ai->ai_addr;memset(saddr,0,sizeof(struct sockaddr_in));saddr->sin_family = AF_INET;saddr->sin_port = atoi(strport);memcpy(&saddr->sin_addr, he->h_addr, sizeof(struct in_addr));ai->ai_next = NULL; }else{he = gethostbyaddr(&addr, sizeof(int), AF_INET );if (!he)return (-1);ai->ai_family = he->h_addrtype;ai->ai_socktype = hints->ai_socktype; //SOCK_RAW, SOCK_STREAM, or SOCK_DGRAM.ai->ai_protocol = hints->ai_proocol; //IPPROTO_TCP or IPPROTO_UDP or 0ai->ai_canonname = he->h_name;ai->ai_addrlen = sizeof(struct sockaddr);if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen)))return (-1);saddr = (struct sockaddr_in*)ai->ai_addr;memset(saddr,0,sizeof(struct sockaddr_in));saddr->sin_family = AF_INET;saddr->sin_port = atoi(strport);memcpy(&saddr->sin_addr, he->h_addr, sizeof(struct in_addr));ai->ai_next = NULL; }return (0);}void fake_freeaddrinfo(struct addrinfo *ai){free(ai->ai_addr);free(ai);}