1.客户端软件设计–基础篇
1.1标识服务器的位置
1.1.1依据域名得到二进制ip地址
int main()
{
/*
* 解析域名用gethostbyname()
* hostent *gethostbyname (const char *__name);
*
*
* hostent的结构
* char *h_name; 正式名字 official name
* char **h_aliases; 别名列表
* int h_addrtype; 地址类型(ipv4/AF_INET或者ipv6/AF_INET6)
* int h_length; 主机ip地址的长度
* char **h_addr_list; 主机ip地址(以网络字节号序存储),若想打印需用inet_ntop()进行地址转换,
* 定义了h_addr为h_addr_list[0]
*
*
* inet_ntop()地址转换
* const char *inet_ntop (int __af, const void *__restrict __cp,char *__restrict __buf, socklen_t __len)
* 参数说明:_af为地址类型,即h_addrtype参数,指明是ipv4还是ipv6
* _restrict_cp为一个以网络字节号存储的ip地址,给出ip
* __restrict __buf是一个char*的32(ipv4)地址,用于存放计算结果,此函数会返回此地址
* socklen_t_len是_restrict_buf的长度,用sizeof即可计算
*
* domainName为域名
* hostent1为解析完域名后的结构体,存储了解析后的信息
* str是用来存储2进制ip地址的
*/
char *domainName, **pptr;
struct hostent *hostent1;
char str[32];
domainName = "www.baidu.com";
//域名解析出错
if ((hostent1 = gethostbyname(domainName)) == NULL)
{
printf(" gethostbyname error for host:%s\n", domainName);
return 0;
}
//解析正确
printf("official hostname:%s\n", hostent1->h_name);//正式名字official name
for (pptr = hostent1->h_aliases; *pptr != NULL; pptr++)
printf(" alias:%s\n", *pptr);//别名
switch (hostent1->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr = hostent1->h_addr_list;
for (; *pptr != NULL; pptr++)
printf(" address:%s\n", inet_ntop(hostent1->h_addrtype, *pptr, str, sizeof(str)));//点分十进制ip
printf(" first address: %s\n", inet_ntop(hostent1->h_addrtype, hostent1->h_addr, str, sizeof(str)));//第一个ip
break;
default:
printf("unknown address type\n");
break;
}
return 0;
}
1.1.2依据名称(应用层协议)查找熟知端口号
void testGetservbyname()
{
/*
* getserverbyname()通过应用层与运输层协议名称获得熟知端口号
* servent *getservbyname (const char *__name, const char *__proto);
* _name代表应用层协议 _proto代表运输层协议
*
* servent结构体
* char *s_name 应用层协议名称
* char *s_proto 运输层协议名称
* int s_port 端口号,需用ntohs进行转换----nthos将一个16位网络字节顺序转换为主机字节顺序
* char **s_aliases 协议的别名
*/
servent *se = NULL;
int i = 0;
se = getservbyname("ftp", "tcp");
if (!se)
return;
printf("name : %s\n", se->s_name);
printf("port : %d\n", ntohs(se->s_port));
printf("proto : %s\n", se->s_proto);
for (i = 0; se->s_aliases[i]; i++)
printf("aliases : %s\n", se->s_aliases[i]);
}
1.1.3依据协议名称返回代表其的整数常量
void testGetprotobyname(){
/*
* getprotobyname()通过运输层协议名称获取 一个代表其的整数常量
* protoent *getprotobyname (const char *__name);
* name:运输层协议名称
*
* protoent
* char * p_name:运输层协议名称
* char * **p_aliases:协议别名
* int p_proto:整数常量
*/
char *name="icmp";
protoent *protoent1=getprotobyname(name);
printf("%s\n",protoent1->p_name);
printf("%d\n",protoent1->p_proto);
}
2.客户端实现–实战篇
2.1确定服务器地址,然后建立连接
//1.标志ip与端口号的结构体--sockaddr_in与sockaddr
struct sockaddr_in:
struct in_addr sin_addr;/* ip地址*/
int sin_port;/* 端口号*/
sin_family;/* 协议族,取PF_INET即可*/
//其中ip地址/端口号可能需要自己通过域名/应用层协议获取
//获取ip地址--将域名映射为ip地址 sin_addr/将电分十进制ip转换为网络ip
struct hosten *phe;
phe=gethostbyname("www.baidu.com");
memcpy(&sin.sin_addr,phe->h_addr,phe->length);
sin.sin_addr.s_addr = inet_addr("192.168.0.1");
//获取端口号--将服务映射为端口 sin_port
struct servent *pse;
pse=getservbyname("http","tcp");
sin.sin_port=pse->s_port;
//2.创建套接字socket,连接
int socetfd=socket(PF_INET,SOCK_STREAM,0);
connect(socketfd,(struct sockaddr *)&sin,sizeof(sin))
//3.......