最大地址结构
struct sockaddr_storage; // 足够大,能够支持任何套接字地址结构
从套接字获取信息
// 获取本地连接的地址
extern int getsockname (int __fd, __SOCKADDR_ARG __addr, socklen_t *__restrict __len) __THROW;
// 获取连接另一侧的地址
extern int getpeername (int __fd, __SOCKADDR_ARG __addr, socklen_t *__restrict __len) __THROW;
字节序转换
// 网络字节序到本地字节序
extern uint32_t ntohl (uint32_t __netlong);
extern uint16_t ntohs (uint16_t __netshort);
// 本地字节序到网络字节序
extern uint32_t htonl (uint32_t __hostlong);
extern uint16_t htons (uint16_t __hostshort);
字节操作函数
// 还有类似的 bset bcmp bcpy
extern void *memset (void *__s, int __c, size_t __n);
extern int memcmp (const void *__s1, const void *__s2, size_t __n);
extern void *memccpy (void *__restrict __dest, const void *__restrict __src, int __c, size_t __n);
地址转换函数,字符串与网络字节序间转换地址
// IPv4
// 废弃 extern in_addr_t inet_addr (const char *__cp) __THROW;
extern int inet_aton (const char *__cp, struct in_addr *__inp) __THROW;
extern char *inet_ntoa (struct in_addr __in) __THROW;
// IPv4 & IPv6
// p: presentation 表达式 n:numeric 数值
extern int inet_pton (int __af, const char *__restrict __cp, void *__restrict __buf) __THROW; // 字符串转二进制
extern const char *inet_ntop (int __af, const void *__restrict __cp, char *__restrict __buf, socklen_t __len ) __THROW; // 二进制转字符串
/*
socket长度可使用已有宏
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
*/
当 read write 时内核缓存区达到极限,此时操作的字节数可能比请求的小,所以需要再次调用read或write
readn writen
ssize_t readn(int fd, void *buf, size_t count) { // 可使用 recv() 与 MSG_WAITALL 代替
size_t nleft = count;
ssize_t nread;
char *bufp = (char *)buf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) { // 系统调用被捕获信号中断
continue;
}
return -1;
} else if (nread == 0) {
return count - nleft;
}
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count) {
size_t nleft = count;
ssize_t nwrite;
char *bufp = (char *)buf;
while (nleft > 0) {
if ((nwrite = write(fd, bufp, nleft)) < 0) {
if (errno == EINTR) {
continue;
}
return -1;
} else if (nwrite == 0) {
continue;
}
bufp += nwrite;
nleft -= nwrite;
}
return count;
}
主机名与IP转换
// 仅支持IPv4,通过主机名查找IP地址
struct hostent *gethostbyname (const char *__name );
// 通过二进制IP地址找到响应主机名
struct hostent *gethostbyaddr (const void *__addr, __socklen_t __len, int __type);
// 查找主机的所有IP和名称信息
struct hostent
{
char *h_name; /* Official name of host. */
char **h_aliases; /* Alias list. */
int h_addrtype; /* Host address type. */
int h_length; /* Length of address. */
char **h_addr_list; /* List of addresses from name server. */
#ifdef __USE_MISC
# define h_addr h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};
通过名称或端口从 /etc/services 中获取服务
struct servent *getservbyname (const char *__name, const char *__proto);
struct servent *getservbyport (int __port, const char *__proto);
{
struct servent *s = getservbyname("ftp", "tcp");
struct servent *s = getservbyport(21, "tcp");
}
// 对服务的描述
struct servent
{
char *s_name; /* Official service name. */
char **s_aliases; /* Alias list. */
int s_port; /* Port number. */
char *s_proto; /* Protocol to use. */
};
/// 支持 IPv4 IPv6
int getaddrinfo (const char *__restrict __name, // 主机名 或 ip
const char *__restrict __service, // 服务名 或 十进制端口号数串
const struct addrinfo *__restrict __req, // 指向某个 addrinfo 结构的指针,填入期望返回值的暗示,可为空
struct addrinfo **__restrict __pai);
const char *gai_strerror (int __ecode); // 返回 getaddrinfo 错误值对应的错误字符串
void freeaddrinfo (struct addrinfo *__ai); // 释放函数中分配的空间,参数为 getaddrinfo 的返回值
// 返回值是一个指向 addrinfo 链表的指针
struct addrinfo
{
int ai_flags; /* Input flags. */
int ai_family; /* Protocol family for socket. */
int ai_socktype; /* Socket type. */
int ai_protocol; /* Protocol for socket. */
socklen_t ai_addrlen; /* Length of socket address. */
struct sockaddr *ai_addr; /* Socket address for socket. */
char *ai_canonname; /* Canonical name for service location. */
struct addrinfo *ai_next; /* Pointer to next in list. */
};
/// 通过端口协议获取服务名称
int getnameinfo (const struct sockaddr *__restrict __sa,
socklen_t __salen, char *__restrict __host,
socklen_t __hostlen, char *__restrict __serv,
socklen_t __servlen, int __flags);
gethostbyname gethostbyaddr getservbyname getservbyport inet_ntoa 是不可重入的,他们都返回指向同一个静态结构的指针,但有已 _r 结尾的支持可重入的版本
UNP 中对 getaddrinfo 的封装
// 通过服务名查找服务的可用地址
int tcp_connect(const char* host, const char* serv) {
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) {
continue;
}
// 查找到一个可用的地址,就退出循环
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
break;
}
close(sockfd);
} while ((res = res->ai_next) != nullptr);
if (res == nullptr) {
return -1;
}
freeaddrinfo(ressave);
return sockfd;
}
int tcp_listen(const char* host, const char* serv, socklen_t* addrlenp) {
int listenfd, n;
const int on = 1;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
return -1;
}
ressave = res;
do {
listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < 0) { // socket失败,继续下一个
continue;
}
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) { // bind成功,退出
break;
}
close(listenfd);
} while ((res = res->ai_next) != nullptr);
if (res == nullptr) { // 遍历完所有的地址,都没有成功
return -1;
}
if (listen(listenfd, 5) < 0) {
close(listenfd);
return -1;
}
if (addrlenp) {
*addrlenp = res->ai_addrlen;
}
freeaddrinfo(ressave);
return listenfd;
}
// 创建未连接的UDP套接字
int udp_client(const char* host, const char* serv, struct sockaddr** saptr,
socklen_t* lenp) {
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) {
continue;
}
break;
} while ((res = res->ai_next) != nullptr);
if (res == nullptr) {
return -1;
}
*saptr = (struct sockaddr*)malloc(res->ai_addrlen);
memcpy(*saptr, res->ai_addr, res->ai_addrlen);
*lenp = res->ai_addrlen;
freeaddrinfo(ressave);
return sockfd;
}
// 创建已连接的UDP套接字
int udp_connect(const char* host, const char* serv) {
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) {
continue;
}
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
break;
}
close(sockfd);
} while ((res = res->ai_next) != nullptr);
if (res == nullptr) {
return -1;
}
freeaddrinfo(ressave);
return sockfd;
}
int udp_server(const char* host, const char* serv, socklen_t* addrlenp) {
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
return -1;
}
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0) {
continue;
}
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) {
break;
}
close(sockfd);
} while ((res = res->ai_next) != nullptr);
if (res == nullptr) {
return -1;
}
if (addrlenp) {
*addrlenp = res->ai_addrlen;
}
freeaddrinfo(ressave);
return sockfd;
}
标签:__,辅助,ai,res,编程,char,int,Linux,hints From: https://www.cnblogs.com/zhh567/p/17603818.html