一、服务与地址的关系
  • 一个端口代表一个服务服务进程因此可以使用服务名来代替端口的使用
  • 像主机一样,服务也通常靠名字来认知
  • 如果我们在程序代码中通过其名字而不是其端口号来指代一个服务,而且从名字到端口号的映射关系保存在一个文件中(通常是/etc/services),那么即使端口号发生变动,我们需修改的仅仅是/etc/services文件中的某 一行,而不必重新编译应用程序。getservbyname函数用于根据给定名字查找相应服务

UNP编程:31---名字与地址转换之(服务解析函数:getservbyname()、getservbyport()、struct  servent)_struct  servent

二、struct  servent结构体
struct servent {
    char  *s_name;     /* official service name */
    char **s_aliases;  /* alias list */
    int    s_port;     /* port number */
    char  *s_proto;    /* protocol to use */
}

参数

  • s_name:服务的官方名称
  • s_aliases:服务别名列表
  • s_port:服务对应的端口号
  • s_proto:与此服务一起使用的协议的名称
三、getservbyname
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
  • 功能:通过服务名与服务的协议类型返回一个struct servent结构体信息

参数

  • 参数1:服务的名称
  • 参数2:服务对应的协议类型

返回值

  • 成功:返回struct servent结构体指针
  • 失败:返回NULL

参数使用的注意事项

  • 参数1必须指定,参数2可以选择忽略或指定
    • 如果protoname指定:如果同时指定了协议(即protoname参数为非空指针),那么指定服务必须有匹配的协议。有些因特网服务既用TCP也用UDP提供(例如DNS),其他因特网服务则仅仅支持单个协议(例如FTP要求使用TCP,而不能使用UDP)
    • 如果protoname未指定:如果protoname未指定而servname指定服务支持多个协议,那么返回哪个端口号取决于实现。通常情况下这种选择无关紧要,因为支持多个协议的服务往往使用相同的TCP端口号和UDP端口号,不过这点 并没有保证
  • 例如:既然FTP仅仅支持TCP,第二个调用和第三个调用等效,第四个调用则会失败
struct servent *sptr;

sptr = getservbyname("domain", "udp"); /* DNS using UDP */
sptr = getservbyname("ftp", "tcp");    /* FTP using TCP */
sptr = getservbyname("ftp", NULL);     /* FTP using TCP */
sptr = getservbyname("ftp", "udp");    /* this call will fail */ 

演示案例

//程序传入主机名和服务名,先根据主机名得到IP
//然后根据服务名获得端口。然后创建一个套接字连接这个IP和端口对应的服务
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<string.h>
#include<netdb.h>
#define MAXLINE 1024

int Socket(int domain,int type,int protocal);

int main(int argc, char **argv)
{
    int					sockfd, n;
    char				recvline[MAXLINE + 1];
    struct sockaddr_in	servaddr;
    struct in_addr		**pptr;
    struct in_addr		*inetaddrp[2];
    struct in_addr		inetaddr;
    struct hostent		*hp;          //保存主机信息
    struct servent		*sp;          //保存服务信息

    if (argc != 3){
        perror("usage: daytimetcpcli1 <hostname> <service>");
        exit(1);
    }
		

    //通过域名解析获得主机的信息
    if ( (hp = gethostbyname(argv[1])) == NULL)//如果没有解析到
    {
        if (inet_aton(argv[1], &inetaddr) == 0) {//将字符串IP转为网络IP
            printf("hostname error for %s: %s\n", argv[1], hstrerror(h_errno));
            exit(2);
        } else {//转换的网络IP存放在inetaddrp[0]中
            inetaddrp[0] = &inetaddr;
            inetaddrp[1] = NULL;
            pptr = inetaddrp;
        }
    } 
    //将解析到的IP地址列表给pptr
    else {
        pptr = (struct in_addr **) hp->h_addr_list;
    }

    //解析服务信息
    if ((sp = getservbyname(argv[2], "tcp")) == NULL){
        printf("getservbyname error for %s\n", argv[2]);
        exit(3);
    }

    //尝试连接这个服务
    for ( ; *pptr != NULL; pptr++)
    {
        sockfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = sp->s_port;
        memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
        printf("trying %s\n",inet_ntoa((struct in_addr)servaddr.sin_addr));

        //连接服务
        if (connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) == 0)
            break;	//连接成功就break
        perror("connect error");
        close(sockfd);
    }
    if (*pptr == NULL){
        perror("unable to connect");
        exit(5);
    }
		
    while ((n = read(sockfd, recvline, MAXLINE)) > 0) {
        recvline[n] = 0;
        fputs(recvline, stdout);
    }
    exit(0);
}

int Socket(int domain,int type,int protocal)
{
    int sockFd=socket(domain,type,protocal);
    if(sockFd<0){
        perror("socket");
        exit(4);
    }
    return sockFd;
}

UNP编程:31---名字与地址转换之(服务解析函数:getservbyname()、getservbyport()、struct  servent)_struct  servent_02
四、getservbyport
#include <netdb.h>
struct servent *getservbyport(int port, const char *proto);

//返回值:成功返回struct servent结构体指针;失败返回NULL
  • 功能:通过端口号与端口对应的服务协议类型返回一个struct servent结构体信息

注意

  • 注意事项一:参数1必须为网络字节序的端口号
struct servent *sptr;

sptr = getservbyport(htons(53),"udp");  /* DNS using UDP */
sptr = getservbyport(htons(21), "tcp"); /* FTP using TCP */
sptr = getservbyport(htons(21), NULL);  /* FTP using TCP */
sptr = getservbyport(htons(21), "udp"); /* this call will fail */ 
  • 注意事项二:一个端口可能有多种协议类型,因此参数2对应于不同的协议类型,返回的信息也不同
    • 必须清楚的是,有些端口号在TCP上用于一种服务,在UDP上却用于完全不同的另一种服 务。例如:表明端口514在TCP上由rsh命令使用,在UDP上却由syslog守护进程使用。512~514范围内的端口都有这个特性

UNP编程:31---名字与地址转换之(服务解析函数:getservbyname()、getservbyport()、struct  servent)_端口号_03