《网络编程》套接字地址与名字转换

前言

在套接字编程中,我们经常使用数字的 IP 地址和端口号进程编程,但是我们平常所熟悉的是一些便于记忆的字符名字,要使这种名字能够为套接字操作函数识别,所以这两者之间必须存在着某种转换关系。本节介绍的是 【主机名
地址】 和 【服务名端口号】 之间的转换。在 Unix 系统中,可以使用函数 gethostbyname、gethostbyaddr 实现【主机名 与 地址】之间的转换;可以使用函数 getservbyname、getservbyport 实现 【服务名 与 端口号
之间的转换。但是前面这些函数只适合在 IPv4 域里面,若要在 IPv4 和 IPv6 实现这些功能,则可以使用 getaddrinfo 函数。

主机名地址 之间的转换

gethostbyname 与  gethostbyaddr 函数

/* 主机名与地址之间转换 */

/*
 * 函数功能:主机名与地址之间转换;
 * 返回值:若成功则返回主机结构指针,若出错则返回NULL;
 * 函数原型:
 */
#include <netdb.h>

struct hostent *gethostbyname(const char *hostname);//将主机名转换为数字地址;
struct hostent *gethostaddr(const char *addr, size_t len, int family);//将数字地址转换为主机名;

/* 函数功能:获取主机信息;
 * 函数原型:
 */
struct hostent *gethostent(void);/* 获取主机信息,并返回hostent结构指针 */
void sethostent(int stayopen);/* 设置主机信息 */
void endhostent(void);
/*
 * 说明:
 * 若主机数据文件没有打开,gethostent会打开它,该函数返回文件的下一条目;
 * 函数sethostent会打开文件,若文件已打开,那么将其回绕;
 * 函数endhostent将关闭文件;
 * 其中hostent结构至少包含如下成员数据:
 */
struct hostent
{
    char    *h_name;        /* official name of host */
    char    **h_aliases;    /* pointer to alternate host name array */
    int     h_addrtype;     /* address type: AF_INET */
    int     h_length;       /* length in bytes of address: 4 */
    char    **h_addr_list;  /* pointer to array of IPv4 address */
};

上面的函数若成功调用,则会返回一个指向 hostent 结构的指针,若出错则返回 NULL,且设置全局变量 h_error 为相应值。一般的 socket 系统调用都将错误信息存储在全局变量 error 中,但是和主机 host 有关的系统调用,则将错误信息存储在 h_error 中,它的取值如下:

  1. HOST_NOT_FOUND:找不到主机;
  2. TRY_AGAIN:重试;
  3. NO_RECOVERY:不可修复性错误;
  4. NO_DATA:指定的名字有效,但是没有记录;

其中,hostent 结构信息之间关系如下图所示:

服务名端口号 之间的转换

getservbyname 与 getservbyport 函数

/* 服务名与端口号之间的转换 */
/*
 * 函数功能:服务名与端口号之间的转换;
 * 返回值:若成功则返回指针,若出错则返回NULL;
 * 函数原型:
 */
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
struct servent *getservbyport(int port, const char *protoname);
struct servent *getservent(void);

void setservent(int stayopen);
void endservent(void);
/*
 * protoname参数若为空,则返回取决与实现,若为非空,则指定协议名称;
 *
 * 其中servent 结构至少包含以下成员:
 */
struct servent
{
    char    *s_name;        /* official service name */
    char    **s_aliases;    /* pointer to alternate service name array */
    int     s_port;         /* port number */
    char    *s_proto;       /* name of protocol */
};

以下是采用上面函数编写的客户端程序:

#include	"unp.h"

int
main(int argc, char **argv)
{
	int					sockfd, n;
	char				recvline[MAXLINE + 1];
	struct sockaddr_in	servaddr;
	struct in_addr		**pptr = NULL;
	struct in_addr		*inetaddrp[2];
	struct in_addr		inetaddr;
	struct hostent		*hp;
	struct servent		*sp;

	if (argc != 3)
		err_quit("usage: %s <hostname> <service>", argv[0]);

    /* 将主机名作为gethostbyname的参数,并获取hostent结构信息 */
	if ( (hp = gethostbyname(argv[1])) == NULL) {
        /* 若gethostbyname获取失败,则使用inet_aton构造地址单元素列表 */
		if (inet_aton(argv[1], &inetaddr) == 0) {
			err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));
		} else {
			inetaddrp[0] = &inetaddr;
			inetaddrp[1] = NULL;
			pptr = inetaddrp;
		}
	} else {/* 若gethostbyname成功,则pptr指向地址链表 */
		pptr = (struct in_addr **) hp->h_addr_list;
	}

    /* 将服务名作为getservbyname的参数,获取servent结构信息 */
	if ( (sp = getservbyname(argv[2], "tcp")) == NULL)
        /* 若失败则退出 */
		err_quit("getservbyname error for %s", argv[2]);

    /* 遍历地址结构列表中的每一个地址 */
	for ( ; *pptr != NULL; pptr++) {
        /* 创建基于TCP套接字 */
		if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
            err_sys("socket error");

        /* 初始化服务器地址信息 */
		bzero(&servaddr, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_port = sp->s_port;/* 端口号由getservbyname获取 */
        /* 复制服务器地址信息,该地址信息由gethostbyname获取 */
		memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
        /* 以下对地址列表中的每个服务器地址尝试连接 */
		printf("trying %s\n",
			   Sock_ntop((SA *) &servaddr, sizeof(servaddr)));

		if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0)
			break;		/* success */
        /* 若地址连接失败,则关闭与该地址相关的套接字 */
		err_ret("connect error");
		close(sockfd);
	}
    /* 检查是否所有服务器地址都连接失败 */
	if (*pptr == NULL)
		err_quit("unable to connect");

    /* 处理函数,读取服务器应答信息,并显示到标准输出 */
	while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
		recvline[n] = 0;	/* null terminate */
		Fputs(recvline, stdout);
	}
	exit(0);
}

【地址 与 主机名】 和 【服务名 与 端口号】之间的转换

getaddrinfo 函数

/* IPv6、IPv4 都可使用 */
/*
 * 函数功能:将 服务名与端口号 和 主机名与地址 之间转换;
 * 返回值:若成功则返回0,若出错则返回非0错误编码;
 * 函数原型:
 */
#include <netdb.h>
#include <sys/socket.h>
int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);

void freeaddrinfo(struct addrinfo *ai);/* 把从getaddrinfo函数动态分配的成员结构内存返回给系统,参数ai是由函数getaddrinfo返回的第一个addrinfo结构 */

const char *gai_strerror(int error);//若getaddrinfo出错时,错误消息只能由该函数输出;

/*
 * 说明:
 * 该函数需要提供主机名或服务名,若只提供其中一个,则另一个必须指定为NULL;
 * addrinfo是一个结构链表,其定义如下:
 */
struct addrinfo
{
    int         ai_flags;       /* customize behavior */
    int         ai_family;      /* address family */
    int         ai_socktype;    /* socket type */
    int         ai_protocol;    /* protocol */
    socklen_t   ai_addrlen;     /* length in bytes of address */
    struct sockaddr *ai_addr;   /* address */
    char        *ai_canonname;  /* canonical name of host */
    struct addrinfo *ai_next;   /* next in list */
};

/*
 * 函数功能:将地址转换成服务名或主机名;
 * 返回值:若成功则返回0,若出错则返回非0值;
 * 函数原型:
 */
#include <netdb.h>
#include <sys/socket.h>
int getnameinfo(const struct sockadd *addr, socklen_t alen, char * host, socklen_t hostlen,
        char * service, socklen_t servlen, unsigned int flags);

/*
 * 说明:
 * addrinfo结构成员:
 * ai_flags 取值如下:
 * (1)AI_PASSIVE      套接字将用于被动打开;
 * (2)AI_CANONNAME    告知getaddrinfo函数返回主机的规范名字;
 * (3)AI_NUMERICHOST  防止任何类型的名字到地址映射,hostname必须是一个地址串;
 * (4)AI_NUMERICSERV  防止任何类型的名字到服务映射,service必须是一个十进制端口号数串;
 * (5)AI_V4MAPPED     若同时指定ai_family值为AF_INET6,若没有可用的AAAA记录,则返回与A记录对应的IPv4映射的IPv6地址;
 * (6)AI_ALL          若同时指定AI_V4MAPPED标志,除了返回与AAAA记录对应的IPv6地址外,还返回与A记录对应的IPv4映射的IPv6地址;
 * (7)AI_ADDRCONFIG   按照所在主机的配置选择返回地址类型;
 */

参考资料:

《Unix 网络编程》

时间: 2024-10-05 06:25:51

《网络编程》套接字地址与名字转换的相关文章

linux网络编程——套接字(socket)入门

1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_family; /* address族, AF_xxx */ char sa_data[14]; /* 14 bytes的协议地址 */ }; sa_family 一般来说,都是"AFINET". sa_data 包含了一些远程电脑的地址.端口和套接字的数目,它里面的数据是杂溶在一切的. 为了处理struct socka

网络编程 套接字socket 及 粘包

网络编程 套接字socket 及 粘包 sockt 初识 五层协议 : 从传输层包括传输层以下 , 都是操作系统帮我们封装的各种head socket套接字充当的就是内置模块的角色 socket 套接字,它存在于传输层与应用层之间的抽象层 避免你学习各层的接口以及协议的使用, socket已经封装好了所有的接口 . 直接使用这些接口或者方法即可 , 使用起来方便,提升开发效率 socket 就是一个模块 , 通过使用学习模块提供的功能 , 建立客户端与服务端的通信 套接字的工作流程(基于TCP和

Linux 网络编程——套接字的介绍

套接字是一种通信机制(通信的两方的一种约定),凭借这种机制,不同主机之间的进程可以进行通信.我们可以用套接字中的相关函数来完成通信过程. 套接字的特性有三个属性确定,它们是:域(domain),类型(type),和协议(protocol). 套接字的域 域指定套接字通信中使用的网络介质.最常见的套接字域是 AF_INET,它是指 Internet 网络,许多 Linux 局域网使用的都是该网络,当然,因特网自身用的也是它. 套接字类型 流套接字(SOCK_STREAM): 流套接字用于提供面向连

&lt;网络编程&gt;套接字介绍

1.端口:IANA(Internet Assigned Numbers Authority)维护着一个端口号分配状况的清单. 众所周知的端口(0-1023):由IANA分配和控制,可能的话,相同的端口号尽可能分配给TCP,UDP和STCP的同一给定服务 已登记的端口(1024-49151):这些端口不受IANA控制, 动态.私用的端口(49152-65535):临时端口. 2.套接字:一个套接字对是一个定义该连接的两个端点的四元组.{本地IP,本地TCP端口号,外地IP,外地TCP端口号} 套接

Python网络编程—套接字属性

socket套接字属性 [1] sockfd.type 套接字类型 [2] sockfd.family 套接字地址类型 [3] sockfd.getsockname() 获取套接字绑定地址 [4] sockfd.fileno() 获取套接字的文件描述符 [5] sockfd.getpeername() 获取连接套接字客户端地址 [6] sockfd.setsockopt(level,option,value)功能:设置套接字选项参数: level 选项类别 SOL_SOCKEToption 具体

UNIX网络编程——套接字选项(心跳检测、绑定地址复用)(转)

/* 设置套接字选项周期性消息检测连通性 心跳包. 心博.主要用于长连接. * 参数:套接字, 1或0开启, 首次间隔时间, 两次间隔时间, 断开次数 */ void setKeepAlive( int iSockfd , int iSockAttrOn, socklen_t iIdleTime , socklen_t iInterval , socklen_t iCount ){ setsockopt( iSockfd , SOL_SOCKET , SO_KEEPALIVE , (const

网络编程-套接字(socket)

一.什么是套接字 套接字(socket)是计算机之前数据传输的工具,是有计算机系统提供的一个组件,是网络数据传输的软件设备. 二.TCP/IP协议 TCP/IP协议栈共分为4层(OSI规范分7层),tcp.udp就是基于socket的一种协议 三.套接字的分类 1.流式套接字(TCP) 它提供了一种可靠的.面向连接的双向通讯方式.适用于传输数据量大的场景. TCP的具有以下三项特征: 传输过程中数据不会丢失,面向连接的套接字会根据接收端的状态传输数据,如果传输出错还会提供重传服务 按序传输数据,

网络编程套接字,osi七层架构各层协议最全讲解

目录 Socket原理 1.什么是Socket 2.网络中进程如何通信 2.1.本地进程间通信 2.2.网络中进程如何通信 3.Socket怎么通信 4.TCP/IP协议 4.1.概念 4.2.TCP的粘包问题以及数据的无边界性: 4.3.TCP数据报结构: 4.4.连接的建立(三次握手): 4.5.TCP四次握手断开连接 4.6.关于 TIME_WAIT 状态的说明 4.7优雅的断开连接–shutdown() 5.OSI模型 6.Socket常用函数接口及其原理 6.1.使用socket()函

UNIX网络编程——套接字选项

http://www.educity.cn/linux/1241288.html 有时候我们需要控制套接字的行为(如修改缓冲区的大小),这个时候我们就要学习套接字选项. int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen) int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)