本文主要是介绍第十六章(一) 套接字初识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
地址格式
一个地址标识一个特定通信域的套接字端点,地址格式与这个特定的通信域相关。为使不同的格式地址能够传入到套接字函数,地址会被强制转化成一个通用的地址结构:
struct sockaddr{sa_family_t sa_family; //address familychar sa_data[]; //variable-length address...
};
在IPv4因特网域(AF_INET)中, 套接字结构地址用以下结构表示:
struct in_addr{in_addr_t s_addr; //IPv4 address
};struct sockaddr_in{sa_family_t sin_family; //address familyin_port_t sin_port; //port numberstruct in_addr_t sin_addr; //IPv4 address
};
最终将转化为结构体 sockaddr 输入到套接字例程中。
有两个函数用于将地址格式进行转换:
char *inet_ntop(int domain, void *addr, char *str, socklen_t size)//用于将网络字节序的二进制地址转化成文本字符串格式
char *inet_pton(int domain, char *str, void *addr)//用于将文本字符串格式转换成网络字节序的二进制地址。
地址查询
<span style="color:#000000;">struct hostent *gethostent(void);//可以找到给定计算机系统的主机信息
struct netent *getnetent(void);//获取网络编号和网络名字</span>
我们可以用以下函数在协议名字和协议编号之间进行映射
struct protoent *getprotobyname(char * name);
struct protoent *getprotobynumber(int proto);
struct protoent *getprotoent(void);
各服务和端口号之间的关系:
struct servent *getservbyname(char *name, char *proto)//将一个服务名映射到一个端口号
struct servent *getserbyport(int pert, char *name)//将一个端口号映射到一个服务名
struct servent *getservent(void);//顺序扫描服务数据库
int getaddrinfo(char *host, char *service, struct addrinfo *hint, struct addrinfo **resg)//用于将一个主机名和一个服务名映射到一个地址//可以提供一个可选的hint来选择符合特定条件的地址
如果getaddrinfo调用失败,不能使用perror或strerror来生成错误信息,而是调用
函数 char *gai_strerror(int error)
如果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。可以导致返回多个addrinfo结构的情形有以下2个:
1. 如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。
2. 如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。
函数 getnameinfo将一个地址转换成一个主机名和一个服务名
以下两个程序说明 getaddrinfo 的使用方法:
注:总是显示bind错误,尚未搜出个明白来。
例一:
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n",
argv[1]);
exit(1);
}struct addrinfo *answer, hint, *curr;
char ipstr[16];
bzero(&hint, sizeof(hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;int ret = getaddrinfo(argv[1], NULL, &hint, &answer);
if (ret != 0) {
fprintf(stderr,"getaddrinfo: &s\n",
gai_strerror(ret));
exit(1);
}for (curr = answer; curr != NULL; curr = curr->ai_next) {
inet_ntop(AF_INET,
&(((struct sockaddr_in *)(curr->ai_addr))->sin_addr),
ipstr, 16);
printf("%s\n", ipstr);
}
freeaddrinfo(answer);
exit(0);
}
例二:
void print_family(struct addrinfo *aip);
void print_type(struct addrinfo *aip);
void print_flags(struct addrinfo *aip);
void print_protocol(struct addrinfo *aip); int main(int ac, char *av[])
{struct addrinfo *ailist, *aip;struct addrinfo hint;if(ac != 3)fprintf(stderr,"Usage : %s nodename service", av[0]);hint.ai_family = 0; //初始化hint结构体用于过滤hint.ai_socktype = 0; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_flags = AI_CANONNAME; //需要一个规范名hint.ai_addr = NULL; hint.ai_next = NULL; hint.ai_canonname = NULL;if(getaddrinfo(av[1].av[2],&hint,&ailist) != 0)printf("%s \n",gai_strerror(error));for(aip=ailist; aip ; aip=aip->ai_next) //打印得到的结果{print_family(aip); print_type(aip); print_protocol(aip); print_flags(aip); printf("\n\thost %s", aip->ai_canonname ?aip->ai_canonname:"-"); if(aip->ai_family == AF_INET) { /* 获取IP地址,并把网络字节序的二进制地址转换为文本字符串地址 */ sinp = (struct sockaddr_in *)aip->ai_addr; addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN); printf(" address %s", addr ? addr:"unknown"); printf(" port %d", ntohs(sinp->sin_port)); } printf("\n"); }return 0;
}void print_family(struct addrinfo *aip)
{ printf(" family-- "); switch(aip->ai_family) { case AF_INET://IPv4 printf("inet"); break; case AF_INET6://IPv6 printf("inet6"); break; case AF_UNIX://UNIX域 printf("Unix"); break; case AF_UNSPEC://未指定 printf("unspecified"); break; default: printf("unknown"); }
}
void print_type(struct addrinfo *aip)
{ printf(" type.. "); switch(aip->ai_socktype) { case SOCK_STREAM: printf("stream"); break; case SOCK_DGRAM: printf("datagram"); break; case SOCK_RAW: printf("raw"); break; case SOCK_SEQPACKET: printf("seqpacket"); break; default: printf("unknown (%d)", aip->ai_socktype); }
} void print_protocol(struct addrinfo *aip)
{ printf(" protocol++ "); switch(aip->ai_protocol) { case IPPROTO_TCP: printf("TCP"); break; case IPPROTO_UDP: printf("UDP"); break; case IPPROTO_SCTP: printf("SCTP"); break; case 0: printf("default"); break; default: printf("unknown (%d)", aip->ai_protocol); }
}
void print_flags(struct addrinfo *aip) { printf(" flags "); if(aip->ai_flags == 0) printf("0"); else { if(aip->ai_flags & AI_PASSIVE) printf(" passive "); if(aip->ai_flags & AI_CANONNAME) printf(" canon "); if(aip->ai_flags & AI_NUMERICHOST) printf(" numhost "); } }
将套接字与地址关联
int bind(int sockfd, struct sockaddr *addr, socklen_t len)//用来关联地址和套接字/*对于使用的地址有以下一些限制:1、在进程正在运行的计算机上,指定的地址必须有效; 不能指定一个其他机器的地址2、地址必须和创建套接字时的地址族所支持的格式相匹配3、地址中的端口号必须不小于1024,除非该进程具有相应的特权(root身份)4、一般只能将一个套接字端点绑定到一个给定地址上,尽管有些协议允许多重绑定*/
getsockname(int sockfd, struct sockaddr *addr, socklen_t alenp)//用来发现绑定到套接字上的地址/*在调用 getsockname之前, 将 alenp 设置为一个指向整数的指针。该整数指定缓冲区 sockaddr 的长度。 返回时, 该整数会被设置返回地址的大小。但是,如果地址和提供的缓冲区长度不匹配,地址会被自动截断而不报错。如果当前没有地址绑定到该套接字,结果是未定的。*/
getpeername(int sockfd, struct sockaddr *addr, socklen_t alenp)//如果套接字已经和对等方连接, 可以用来找到对方的地址。
建立连接
int connect(int sockfd, struct sockaddr *addr, socklen_t len)//在请求服务的进程套接字(客户端)和提供服务的进程套接字之间建立一个连接。//所填入的地址是我们想要与之通信的服务器地址。
监听套接字
int listen(int sockfd, int backlog)//用于宣告服务器愿意接受连接请求//一旦系统满,就会拒绝多余的连接请求,所以backlog的值应该基于服务器期望负载和处理量来选择,其中处理量是指接受连接请求与启动服务的数量。
获取套接字信息
int accept(int sockfd, struct sockaddr *addr, socklen_t len)/* 一旦服务器调用了listen, 所用的套接字就接收连接请求。使用 accept 函数获得连接请求并建立连接返回的文件描述符是连接到调用connect的客户端的套接字描述符如果服务器调用accept,并且当前没有连接请求,服务器会阻塞知道一个链接请求到来。另外,服务器可以调用 poll 或 select 来等待一个请求的到来*/
这篇关于第十六章(一) 套接字初识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!