C语言 网络地址

22 篇文章 0 订阅
4 篇文章 0 订阅
网络编程中,或多或少都会与网络地址打交道,我们从易到难,再来一个三层境界,菜鸟级,入门级,进阶级。下面一一说明。

菜鸟级:
菜鸟都知道,网络编程中要用到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_name
Official 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_aliases
Null-terminated array of alternate names.
h_addrtype
Type of address being returned.
h_length
Length of each address, in bytes.
h_addr_list
Null-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_flags
Flags that indicate options used in the getaddrinfo function. See AI_PASSIVE, AI_CANONNAME, and AI_NUMERICHOST.
ai_family
Protocol family, such as PF_INET.
ai_socktype
Socket type, such as SOCK_RAW, SOCK_STREAM, or SOCK_DGRAM.
ai_protocol
Protocol, such as IPPROTO_TCP or IPPROTO_UDP. For protocols other than IPv4 and IPv6, set ai_protocol to zero.
ai_addrlen
Length of the ai_addr member, in bytes.
ai_canonname
Canonical name for the host.
ai_addr
Pointer to a sockaddr structure.
ai_next
Pointer 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() function
memset(&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 host
if ((retVal = getaddrinfo(ip, port, &aiHints, &aiList)) != 0) {
printf("getaddrinfo() failed.n");
}
------------------------------------------------------------
...
//-----------------------------------------
// Declare and initialize variables
struct 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 function
saGNI.sin_family = AF_INET;
saGNI.sin_addr.s_addr = inet_addr(ip);
saGNI.sin_port = htons(port);

//-----------------------------------------
// Call getnameinfo
if ((retVal = getnameinfo((SOCKADDR *)&saGNI,
sizeof(sockaddr),
hostName,
256,
servInfo,
256,
NI_NUMERICSERV)) != 0) {
printf("getnameinfo() failed.n");
printf("Error #: %ldn", WSAGetLastError());
}

这几个函数就比较高级,与前面两级的的函数相比,已经上升到应用而不是停留在网络,而且用户自主管理内存,这样的好处是支持可重入的并发使用,到了这个阶段,也不需要多说了,手册就在那里,自己看了!
下面在在第一二阶段的函数基础上对getaddrinfo的简单实现!作为对此文的总结!
int
fake_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 0
ai->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 0
ai->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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值