通过服务器的名字而不是服务器端口号来认知它,而且如果从主机到端口号的映射包含在一个文件中(一般是/etc/services),则如果端口号改变,我们所需做的所有改动就是改动文件/etc/services中的一行,而不是重新编译应用程序。
getservbyname函数原型为:
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
成功返回非空指针,失败返回空指针
此函数返回一个指向下面所示结构的指针:
struct servent
{
char *s_name; //official service name
char **s_aliases; // alias list
int s_port;// port number, network-byte order
char *s_proto;// protocol to use
};
服务名必须指定,如果指定了一个协议(即protoname为非空指针),则结果表项也必须有匹配的协议。如果protoname没有指定且服务支持多个协议,则返回哪个端口是依赖于实现的。一般来说这没有关系,因为支持多个协议的服务常常使用相同的TCP和UDP端口号,但并没有保证
结构servent中我们关心的主要成员是端口号,由于端口号是以网络字节序返回的,在将它存储于套接口地址结构时,绝对不能调用htons
对此函数的典型调用是:
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
由于FTP仅支持TCP,所以第二个和第三个调用 是相同的,第四个调用将失败。
下一个函数getservbyport在给定端口号和可选协议后查找相应的服务
#include <netdb.h>
struct servent *getservbyport(int port, const char *protoname);
成功返回非空指针,失败返回空指针
port为网络字节序。对此函数的典型调用是:
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
代码如下:
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 128
char *sock_ntop(const struct sockaddr* sa, socklen_t salen)
{
char portstr[7];
static char str[MAXLINE];
switch (sa->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in*)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return NULL;
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port));
strcat(str, portstr);
}
return str;
}
}
}
int main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
struct in_addr **pptr;
struct hostent *hp;
struct servent *sp;
if (argc != 3) {
printf("usage: execname <hostname> <service> \n");
return -1;
}
if ((hp = gethostbyname(argv[1])) == NULL) {
printf("hostname error for %s:%s\n", argv[1], hstrerror(h_errno));
return -1;
}
if ((sp = getservbyname(argv[2], "tcp")) == NULL) {
printf("getservbyname error for %s:%s\n", argv[2], hstrerror(h_errno));
return -1;
}
pptr = (struct in_addr**)hp->h_addr_list;
for (; *pptr != NULL; pptr++) {
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
printf("socket error:%s\n", strerror(errno));
return -1;
}
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("addr=%s, port=%d\n", inet_ntoa(**pptr), ntohs(sp->s_port));
printf("trying %s\n", sock_ntop((struct sockaddr*)&servaddr, sizeof(servaddr)));
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == 0) break;
printf("connect error:%s\n", strerror(errno));
close(sockfd);
}
if (*pptr == NULL) {
printf("unable to connect\n");
return -1;
}
while ((n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0;
fputs(recvline, stdout);
}
return 0;
}
输出为: