Linux下C Socket编程基础API


1. 地址类API

1.1 字节序

分为大端字节序和小端字节序,区别是大端字节序更符合人类的视觉,即高位字节存储在低位地址处,低位字节存储在高位字节处,而小端序符合计算机的口味,即高位字节存储在高位地址处,低位字节存储在低位地址处。

一般PC使用小端字节序,所以小端字节序也称为主机字节序;网络数据传送使用大端字节序,所以大端字节序也称为网络字节序。

一个简单的判断程序:

void byteorder()
{
    union byteorder {
        short value;
        char  union_byte[sizeof(short)];
    } test;

    test.value = 0x0102;

    if ((test.union_byte[0] == 1) && (test.union_byte[1] == 2))
        printf("Big endian\n");
    else if ((test.union_byte[0] == 2) && (test.union_byte[1] == 1))
        printf("Little endian\n");
    else
        printf("Unknown\n");
}

1.2 通用Socket地址

#include <bits/socket.h>
struct sockaddr
{
	sa_family_t sa_family; 			// 地址族类型,通常与协议族类型对应,
	char 		sa_data[14]; 		// Family-specific address info, 存放socket地址
};

常见的协议族(protocol family,也称domain)

协议族 地址族 描述
PF_UNIX AF_UNIX UNIX 本地域协议族
PF_INET AF_INET TCP/IPv4 协议族
PF_INET6 AF_INET6 TCP/IPv6 协议族

AF_*与PF_*的宏都定义在bits/socket.h中,两者值相同,所以可以混用。
协议族及其地址值

协议族 地址值含义和长度
PF_UNIX 文件的路径名,长度可达108bytes
PF_INET 16 bits 端口号和 32 bits IPv4 地址,共6字节
PF_INET6 16 bits 端口号,32 bits 流标识,128 bits IPv6 地址,32 bits范围 ID,共26 字节

由上,sockaddr 放不下多数协议族地址,而设计了下面的sockaddr_storage。

#include <bits/socket.h>
struct sockaddr_storage
{
	sa_famiy_t 		  sa_family;
	unsigned long int __ss_align;
	char 			  __ss_padding[128 - sizeof __ss_align];
};

不仅提供了足够大的空间用于存放地址,而且是内对齐的。

但是因为函数都传递struct sockaddr*类型参数,所以都需要进行强制类型转换,而使用domain判断具体是使用的哪一个。

1.3 专用socket地址

1.3.1 UNIX 本地域协议族

#include <sys/un.h>
struct sockaddr_un
{
	sa_family_t sin_family;	// 地址族:AF_UNIX
	char sun_path[108];		// 文件路径名
};

1.3.2 IPv4

struct sockaddr_in
{
	sa_family_t		sin_family; // 地址族:AF_INET
	u_int16_t 		sin_port;	// 端口号,要用网络字节序表示
	struct in_addr	sin_addr;	// IPv4地址结构体
};
struct in_addr 
{
	u_int32_t s_addr;	// IPv4 地址,要用网络字节序表示
};

1.3.3 IPv6

struct sockaddr_in6
{
	sa_family_t 	sin6_family;	// 地址族:AF_INET6
	u_int16_t 		sin6_port;		// 端口号,要用网络字节序表示
	u_int32_t		sin6_flowinfo;	// 流信息,应设置为0
	struct in6_addr	sin6_addr;		// IPv6 地址结构体
	u_int32_t		sin6_scope_id;	// socpe ID, 尚处于实验阶段
};
struct in6_addr
{
	unsigned char sa_addr[16];		// IPv6 地址,要用网络字节序表示
};

1.4 IP 地址转换函数

1.4.1 适用于 IPv4 地址

#include <arpa/inet.h>
/*
 * inet_addr: Internet address manipulation routines,converts the Internet host address cp 
 * 		from IPv4 numbers-and-dots notation into binary  data  in  network  byte  order. 
 * 		【用点分十进制字符串表示的IPv4 地址转化为用网络字节序整数表示的IPv4地址。】
 * @params
 * 	strptr:	IPv4地址
 * @return:
 * 	失败返回INADDR_NONE(其实是-1,但-1对应地址255.255.255.255)
 */
in_addr_t inet_addr(const char* strptr);

/*
 * inet_aton: converts the Internet host address cp from the IPv4 numbers-and-dots notation
 *		into binary form (in network byte order) and stores it in the structure that inp points to.
 *      【点分十进制的IPv4地址转换为二进制形式,其实就是网络字节序的整数形式】
 * @params:具体形式请查看Man Pages
 *  cp: 		IPv4点分十进制形式
 * 	inp: 		IPv4网络字节序形式
 * @return:
 *  returns nonzero if the address is valid, zero if not.
 * 		【地址参数有效返回非0,否则返回0】
 */
int inet_aton(const char* cp, struct in_addr*inp);
/*
 * inet_ntoa: converts the Internet host address in, given in network byte order,
 *      to a string in IPv4 dotted-decimal notation. 
 *     【网络字节序的IPv4地址转换为点分十进制】
 * @params:
 * 	in: 	IPv4网络字节序地址(struct in_addr)
 * @return:
 * 	char*: The string is returned in  a  statically  allocated
 * 	 	buffer, which subsequent calls will overwrite.
 * 		【IPv4点分十进制字节序,静态内存,不可重入】
 */
char* inet_ntoa(struct in_addr in);

1.4.2 适用于 IPv4 和 IPv6 地址

#include <arpa/inet.h>
/*
 * inet_pton: converts  the character string src into a network address structure in the af
 *      address family, then copies the network address structure to dst. 
 * 		【把src 字符串类型转换为af指定的网络地址类型,并保存在dst】
 * @params:
 * 	af: 	address family, AF_INET or AF_INET6【地址族】
 *  src: 	字符串类型的IP地址
 *  dst: 	网络字节序类型的IP地址
 * @return:
 * 	returns 1 on success (network address was successfully converted).  0 is returned
 *      if src does not contain a character string representing a valid network address in the speci‐
 *      fied address family.  If af does not contain a valid address family, -1 is returned and errno
 *      is set to EAFNOSUPPORT.
 *		【成功1,src不对0,af不对-1】
 */
int inet_pton(int af, const char* src, void* dst);

/*
 * inet_ntop: converts  the  network  address structure src in the af address family into a
 *       character string. 
 * @params:
 * 	af: 	address family, AF_INET or AF_INET6【地址族】
 * 	src: 	网络字节序类型的IP地址
 * 	dst: 	字符串类型的IP地址
 * 	size: 	The caller specifies the number of bytes available in this buffer in
 *      	the argument size.
 * 			【指定目标存储单元的大小】
 * @return:
 * 	On success, inet_ntop() returns a non-null pointer to dst.  
 * 	NULL is returned if there was an error, 
 * 		with errno set to indicate the error.
 * 		【成功返回目标存储单元地址,失败NULL,并设置errno】
 */	
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);


2. Socket API

因为 UNIX 哲学:所有东西都是文件,Socket 本质也是一个文件。

2.1 创建Socket

#include <sys/socket.h>
#include <sys/types.h>
/*
 * socket: create an endpoint for communication
 * 		【创建通信端点】
 * @params
 * 	domain: 	specifies a communication domain【指定协议族】
 * 		AF_INET(IPv4), AF_INET6(IPv6), AF_UNSPEC(IPv4或IPv6), ...
 * 	type: 		specifies the communication semantics【指定服务类型】
 * 		SOCK_STREAM, SOCK_DGRAM, ...
 * 	protocol: 	specifies a particular protocol to be used with the socket.【指定协议类型,一般都设置为0,表示使用默认协议】
 * 		IPPROTO_TCP, IPPROTO_UDP, ...
 * @return:
 * 	On success, a file descriptor for the new socket is returned.  
 * 		【成功,返回一个Socket文件描述符】
 * 	On error, -1 is returned, and errno is set appropriately.
 * 		【失败,返回-1,并设置errno】
 */
int socket(int domain, int type, int protocol);

2.2 命名Socket

#include <sys/socket.h>
#include <sys/types.h>
/*
 * bind: bind a name to a socket
 * 		【命名socket:将一个socket与socket地址绑定】
 * @params:
 * 	sockfd: 	socket file descriptor【socket文件描述符】
 * 	addr: 		address【给socket绑定的地址】
 * 	addrlen: 	specifies the size, in bytes, of the address structure pointed to by addr.【地址长度】
 * @return:
 * 	On success, zero is returned.  
 * 		【成功,返回0】
 * 	On error, -1 is returned, and errno is set appropriately.
 * 		【失败,返回-1,并设置errno】
 */
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

2.3 监听Socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/*
 * listen: marks the socket referred to by sockfd as a passive socket, that is, as a socket
 *      that will be used to accept incoming connection requests using accept(2).
 * 		【监听socket】
 * @params:
 * 	sockfd: 	socket file descriptor,socket文件描述符
 * 	backlog: 	提示内核监听队列的最大长度,典型值为5
 * @return:
 * 	On success, zero is returned.  
 * 	On error, -1 is returned, and errno is set appropriately.
 */
int listen(int sockfd, int backlog);

2.4 接受连接

#include <sys/types.h>
#include <sys/socket.h>
/*
 * accept: accept a connection on a socket. 
 * 		used with connection-based socket types 
 * 		(SOCK_STREAM, SOCK_SEQPACKET). 
 * 		【用于有连接的类型】
 * @params:
 * 	sockfd:		socket file descriptor
 * 	addr:		address【被接受连接的远端socket地址】
 * 	addrlen:	length of address【socket地址长度】
 * @return:
 * 	On success, these system calls return a nonnegative integer 
 * 		that is a file descriptor for the accepted socket.  
 * 	On error, -1 is returned, and errno is set appropriately.
 */
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

2.5 发起连接

/*
 * connect - initiate a connection on a socket
 * 		【初始化Socket连接,本函数会自动绑定一个未使用的端口。】
 * 		【不过一般也就是在Client用,Server提供的服务不会动态分配端口的。】
 * @params:
 * 	sockfd: 	socket file descriptor【Socket文件描述符,是socket()函数的返回值】
 * 	addr: 		address【地址】	
 * 	addrlen: 	the size of addr【地址的大小】
 * @return:
 * 	If the connection or binding succeeds, zero is returned.  
 * 		【成功,返回0】
 * 	On error, -1 is returned, and errno is set appropriately.
 * 		【失败,返回-1,并设置errno】
 */
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

2.6 关闭Socket


/*
 * close: close a file descriptor
 * 		【关闭文件,其实就是文件I/O的close()函数,不过并非立即关闭,
 * 		而是文件的引用计数减一,减为0才关闭】
 * @params:
 * 	fd: 		file descriptor
 * @return:
 * 	Returns zero on success.  
 * 		【成功返回0】
 * 	On error, -1 is returned, and errno is set appropriately.
 * 		【失败返回-1,并设置errno】
 */
#include <unistd.h>
int close(int fd);

/*
 * shutdown: shut down part of a full-duplex connection,立即终止连接
 * @params:
 * 	sockfd: 	socket file descriptor
 * 	how: 
 * 		If how is SHUT_RD, further receptions will be disallowed.   
 * 			【关闭读】
 * 		If how is SHUT_WR, further transmissions will be disallowed. 
 * 			【关闭写】
 * 		If how is SHUT_RDWR, further receptions and transmissions will be disallowed.
 * 			【关闭读和写】
 * @return:
 * 	On success, zero is returned.  
 * 	On error, -1 is returned, and errno is set appropriately.
 */
#include <sys/socket.h>
int shutdown(int sockfd, int how);

2.7 数据读写

2.7.1 TCP 数据读写

#include <sys/socket.h>
#include <sys/types.h>
/*
 * send - transmit a message to another socket
 * 		【发送消息】
 * @params:
 * 	sockfd: socket file descriptor【Socket文件描述符,是socket()函数的返回值】
 * 	buf: 	buffer【发送数据的首地址】
 * 	len: 	buffer length【发送数据的长度】
 * 	flags: 	is the bitwise OR of zero or more of the following flags.【0:此时功能同write或以下标志】
 * 		MSG_CONFIRM
 *      MSG_DONTROUTE
 * 	    MSG_DONTWAIT
 *      MSG_EOR
 * 		MSG_MORE
 * 		MSG_NOSIGNAL
 *      MSG_OOB
 * @return:
 * 	On success, these calls return the number of bytes sent.  
 * 		【成功,返回发送的字节数,不一定发送完了bufLen】
 * 	On error, -1 is returned, and errno is set appropriately.
 * 		【失败,返回-1,并设置errno】
 */
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

/*
 * recv - receive a message from a socket
 * 		【接收消息】
 * @params:
 * 	同send() 
 * @return:
 * 	These calls return the number of bytes received, or -1 if an error occurred.
 * 		【成功返回接收到的字节数;0,意味着通信对方关闭连接了;失败,返回-1,并设置errno】
 */
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

2.7.2 UDP 数据读写

#include <sys/types.h>
#include <sys/socket.h>
/*
 * sendto: send a message on a socket
 * @params:
 * 	sockfd: 	socket file descriptor
 * 	buf: 		buffer
 * 	len: 		length of buffer
 * 	flags: 		同上
 * 	dest_addr:
 * 	addrlen: 
 * @return:
 */
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
               
/*
 * recvfrom: receive messages from a socket.
 * @params:
 * 	sockfd: socket file descriptor
 * 	buf: 		buffer,存储收到的信息
 * 	len: 		size of buffer
 * 	flags: 		同上
 * 	src_addr:
 * 		If src_addr is not NULL, and the underlying protocol provides the source address of the 
 * 			message,  that  source  address  is  placed in the buffer pointed to by src_addr.
 * 		If  the caller is not interested in the source address,
 * 			 src_addr and addrlen should be specified as NULL.
 * 	addr_len: 	地址长度
 * @return:
 *  These calls return the number of bytes received, or -1 if an error occurred.  In the event of
 *      an error, errno is set to indicate the error.
 * 	Datagram  sockets in various domains (e.g., the UNIX and Internet domains) permit zero-length
 *      datagrams. When such a datagram is received, the return value is 0.
  * The value 0 may also be returned if the requested number of bytes to receive from a stream
  *     socket was 0.

 */
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

2.7.3 通用数据读写函数

#include <sys/socket.h>
/*
 * sendmsg: send a message on a socket
 * @params:
 * 	sockfd: 	socket file descriptor
 * 	msg:		msghdr 结构类型指针
 * 				详情参见下面的struct msghdr
 * 	flags: 		同上
 * @return:
 */
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

struct msghdr {
    /*
     * optional address【Socket地址】 
     * 	TCP协议中无用,必须设置为0
	 */
    void         *msg_name;
    /* 
     * size of address【Socket地址长度】 
     */
    socklen_t     msg_namelen;    
    /* 
     * scatter/gather array【分散的内存块】 
     * 	struct iovec封装了一块内存起始位置和长度,定义见下文 
     */
    struct iovec *msg_iov;        
    /* 
     * # elements in msg_iov【分散内存块的数量】 
     */
    size_t        msg_iovlen;     
    /* 
     * ancillary data, see below【指向辅助数据的起始位置】 
     */
    void         *msg_control;    
    /*
     * ancillary data buffer len【辅助数据的大小】 
     */
    size_t        msg_controllen; 
    /* 
     * flags (unused)【复制函数中的flags参数,并在调用过程中更新 
     */
    int           msg_flags;      
};

/*
 * struct iovec: 封装了一块内存的起始位置和长度
 */
struct iovec
{
	void* 	iov_base; 	/* 内存起始地址 */
	size_t	iov_len;	/* 这块内存的长度 */
};

2.8 带外标记

/*
 * sockatmask: 判断 sockfd 是否处于带外标记,即下一个读取到的数据是否是带外数据
 * @params:
 * 	sockfd: 	socket file descriptor
 * @return:
 * 	是则返回1,可以使用带MSG_OOB标记的recv调用来接收带外数据
 * 	否则返回0
 */
#include <sys/socket.h>
int sockatmask(int sockfd);

2.9 地址信息函数

#include <sys/socket.h>
/*
 * getsockname: get socket name. 
 * 		the current address to which the socket sockfd is bound, 
 * 		in the buffer pointed to by addr. 
 * 		【获取sockfd对应的本端socket地址,并将其存储在addr指定的内存中】
 * @params:
 * 	sockfd: 	socket file descriptor
 * 	addr: 		address of the peer connected to the socket sockfd
 * 	addrlen: 	indicate the amount of space pointed to by addr. 
 * @return:
 * 	On success, zero is returned.  
 * 	On error, -1 is returned, and errno is set appropriately.
 */
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/*
 * getpeername: get name of connected peer socket. 
 * 		the address of the peer connected to the socket sockfd, 
 * 		in the buffer pointed to by addr. 
 * 		【获取sockfd对应的远端socket地址,并将其存储在addr指定的内存中】
 * @params:
 * 	sockfd: 	socket file descriptor
 * 	addr: 		address of the peer connected to the socket sockfd
 * 	addrlen: 	indicate the amount of space pointed to by addr. 
 * @return:
 * 	On success, zero is returned.  
 * 	On error, -1 is returned, and errno is set appropriately.
 */
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

2.10 Socket 选项

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
/* 
 * getsockopt - get options on sockets
 * @params:
 * 	sockfd:socket file descriptor
 * 	level: 		指定要操作哪个协议的选项(即属性),如IPv4、IPv6、TCP等
 * 	optname: 	指定选项的名字见:表 socket选项
 * 	optval: 	值
 * 	optlen: 	值的长度
 * @return:
 * 	On success, zero is returned for the standard options.  
 * 	On error, -1 is returned, and errno is set appropriately.
 *  Netfilter allows the programmer to define custom socket options with associated handlers; 
 * 		for such options, the return value on success is the value returned by the handler.
 */
int getsockopt(int sockfd, int level, int optname,
               void *optval, socklen_t *optlen);

/*
 * setsockopt - set options on sockets
 * @params:
 * 	sockfd:socket file descriptor
 * 	level: 		指定要操作哪个协议的选项(即属性),如IPv4、IPv6、TCP等
 * 	optname: 	指定选项的名字见:表 socket选项。
 * 	optval: 	值
 * 	optlen: 	值的长度
 * @return:
 * 	On success, zero is returned for the standard options.  
 * 	On error, -1 is returned, and errno is set appropriately.
 *  Netfilter allows the programmer to define custom socket options with associated handlers;
 * 		for such options, the return value on success is the value returned by the handler.
 * 	
 */
int setsockopt(int sockfd, int level, int optname,
               const void *optval, socklen_t optlen);

表 socket 选项

level option name 数据类型 说明
SOL_SOCKET(通用Socket选项,与协议无关) SO_DEBUG int 打开调试信息
SO_REUSEADDR int 重用本地地址
SO_TYPE int 获取socket类型
SO_ERROR int 获取并清除socket错误标志
SO_DONTROUTE int 不查看路由表,直接将数据发送给本地局域网内的主机。含义和send系统调用的MSG_DONTROUTE标志类似
SO_RCVBUF int TCP接收缓冲区大小
SO_SNDBUF int TCP发送缓冲区
SO_KEEPALIVE int 发送周期性保活报文以维持连接
SO_OOBINLINE int 接收到带外数据将存留在普通数据的输入队列中(在线存留),此时我们不能使用带MSG_OOB标志的读操作来读取带外数据(而应该像读取普通数据那样读取带外数据)
SO_LINGER linger 若有数据待发送,则延迟关闭
SO_RCVLOWAT int TCP接收缓存区低水位标记
SO_SNDLOWAT int TCP发送缓存区低水位标记
SO_RCVTIMEO timeval 接收数据超时
SO_SNDTIMEO timeval 发送数据超时
IPPROTO_IP(IPv4选项) IP_TOS int 服务类型
IP_TTL int 存活时间
IPPROTO_IPv6(IPv6选项) IPv6_NEXTHOP sockaddr_in6 下一条IP地址
IPv6_RECVPKTINFO int 接收分组信息
IPv6_DONTFRAG int 禁止分片
IPv6_RECVTCLASS int 接收通信类型
IPPROTO_TCP(TCP选项) TCP_MAXSEG int TCP最大报文段大小
TCP_NODELAY int 禁止Nagle算法

2.10.1 SO_REUSEADDR 选项

强制使用被处于TIME_WAIT状态的连接占用的socket地址。示例:

......
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (scokfd < 0)
{
	perror("socket()");
	exit(1);
}

int reuse = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse);

struct sockaddr_in address;
memset(&address, 0, sizeof address);
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
int ret = bind(sock, (struct sockaddr*) &address, sizeof address);
......

2.10.2 SO_RCVBUF 和 SO_SNDBUF 选项

分别表示TCP接收缓冲区和发送缓冲区的大小。
TCP接收缓冲区最小值是256字节,发送缓冲区最小值是2048字节。

2.10.3 SO_RCVLOWAT 和 SO_SNDLOWAT 选项

分别表示TCP接收缓冲区和发送缓冲区的低水位标记。
被I/O复用系统调用用来判断socket是否可读或可写。

2.10.4 SO_LINGER 选项

控制close系统调用再关闭TCP连接时的行为。此时需要传递一个linger类型的结构体
linger类型的结构体

#include <sys/socket.h>
struct linger
{
	int	l_onoff; 	/* 开启(非0)还是关闭(0)该选项 */
	int l_linger; 	/* 滞留时间 */
};
  • l_onoff 等于 0。此时SO_LINGER 选项不起作用,close用默认行为来关闭socket
  • l_onoff不为 0,l_linger等于 0。此时close系统调用立即返回,TCP模块将丢弃被关闭的socket对应的TCP发送缓冲区中残留的数据,同时给对方发送一个复位报文段。因此这种情况给服务器提供了一个异常终止一个连接的方法。
  • l_onoff不为 0,l_linger大于 0。此时close的行为取决于两个条件:
    • 一是被关闭的socket对应的TCP发送缓冲区中是否还有残留的数据;
    • 二是该socket是阻塞的还是非阻塞的。
      对于阻塞的socket,close将等待一段长为l_linger的时间,知道TCP模块发送完所有残留数据并得到对方的确认。如果这段时间内TCP模块没有发送完残留书并得到对方的确认,那么close系统调用将返回-1并设置errno位EWOULDBLOCK。如果socket非阻塞的,close将立即返回,此时我们需要根据其返回值和errno来判断残留数据是否已经发送完毕。

3 网络信息API

3.1 gethostbyname 和 gethostbyaddr

但是实际上这两个函数已经 obsolete。所以不再详细介绍了,推荐使用 getaddrinfo(3), getnameinfo(3), 和gai_strerror(3)。

#include <netdb.h>
struct hostent *gethostbyname(const char *name);
#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);

3.2 getservbyname 和 getservbyport

#include <netdb.h>
/*
 * getservbyname: returns a servent structure for the entry from the database 
 * 		that matches the service name using protocol proto.
 * 		【根据名称获取某个服务的完整信息】
 * @params:
 * 	name: 		service name,目标服务名称
 * 	proto: 		protocol,服务类型
 * @return:
 * 	return a pointer to a statically allocated servent structure, or NULL if an
 *      error occurs or the end of the file is reached.
 * 	返回servent结构类型
 */
struct servent *getservbyname(const char *name, const char *proto);

/*
 * getservbyport: returns  a  servent structure for the entry from the database 
 * 		that matches the port port (given in network byte order) using protocol proto.
 * 		【根据端口获取某个服务的完整信息】
 * @params:
 * 	port: 		端口
 * 	proto: 		协议
 * @return:
 * 	return a pointer to a statically allocated servent structure, 
 * 		or NULL if an error occurs or the end of the file is reached.
 */
struct servent *getservbyport(int port, const char *proto);

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

3.3 getaddrinfo

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

/*
 * getaddrinfo: Given  node  and  service,  which identify an Internet host and a service, 
 * 		getaddrinfo() returns one or more addrinfo structures, each of which
 * 		contains an Internet address that can be specified in a call to bind(2) or connect(2).
 * @params:
 * 	node: 		identify an Internet host【可以是主机名,也可以是IP地址】
 * 	service: 	identify an Internet service【接收服务名或者十进制端口号】
 * 	hints: 		points to an addrinfo structure that specifies criteria for 
 * 				selecting the socket address structures returned in the list pointed to by res. 
 * 				【提示,对getaddrinfo的输出进行更精确的控制,可以为NULL,表示允许getaddrinfo反馈任何可用的结果】
 * 	res: 		result, 【函数反馈的地址结果,可能不止一个】
 * @return:
 * 	On success, 0 is returned, and node and service names, if requested, are filled with 
 * 		null-terminated strings, possibly  truncated  to  fit  the specified buffer lengths.  
 * 	On error, one of the following nonzero error codes is returned:
 * 		EAI_AGAIN		The name could not be resolved at this time.  Try again later.
						【调用临时失败,提示应用程序过后再试】
 *  	EAI_BADFLAGS	The flags argument has an invalid value.
 * 						【非法的ai_flags值】
 *		EAI_FAIL        A nonrecoverable error occurred.
 *						【名称解析失败】
 *		EAI_FAMILY      The address family was not recognized, or the address length 
 *						was invalid for the specified family.
 *						【不支持的ai_family参数】
 *      EAI_MEMORY      Out of memory.
 * 						【内存分配失败】
 *      EAI_NONAME      The name does not resolve for the supplied arguments.  
 * 						NI_NAMEREQD is set and the host's name cannot be located, 
 * 						or neither hostname nor service name were requested.
 * 						【非法的主机名或服务名】
 *      EAI_OVERFLOW    The buffer pointed to by host or serv was too small.
 * 						【用户提供的缓冲区溢出。仅发生在getnameinfo中】
 * 		EAI_SERVICE		【没有支持的服务,比如用数据报服务类型来查找ssh服务。因为ssh服务只能使用流服务】
 * 		EAI_SOCKTYPE	【不支持的服务类型。如果hints.ai_socktype和hints.ai_protocol不一致,则会触发此类错误】
 *      EAI_SYSTEM      A system error occurred.  The error code can be found in errno.
 * 						【系统错误,错误值存储在errno中】
 */
int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints, struct addrinfo **res);

struct addrinfo {
  	int              ai_flags;		/* 取值OR(按位或)或者表 ai_flags成员*/
    int              ai_family;		/* 地址族 */
    int              ai_socktype;	/* 服务类型,SOCK_STREAM或SOCK_DGRAM */
    int              ai_protocol;	/* 指具体的网络协议,通常为0 */
    socklen_t        ai_addrlen;	/* socket地址ai_addr的长度 */
    struct sockaddr *ai_addr;		/* 指向socket地址 */
    char            *ai_canonname;	/* 主机的别名 */
    struct addrinfo *ai_next;		/* 指向下一个sockinfo结构的对象 */
};
表 ai_flags成员
选项 含义
AI_PASSIVE 在hints参数中设置,表示调用者是否会将取得的socket地址用于被动打开。服务器通常需要设置它,表示接受任何本地socket地址上的服务请求。客户端程序不能设置它。
如果在getaddrinfo()中,第一个参数是NULL,那么返回的地址信息结构中地址都是恰当的"any"地址常量——INADDR_ANY(IPv4)或者是IN6ADDR_ANY_INIT(IPv6)。
AI_CANNONAME 在hints参数中设置,告诉getaddrinfo函数返回主机的别名。在DNS中是指CNAME(canonical name)。
AI_NUMERICHOST 在hints参数中设置,表示hostname必须是用字符串表示的IP地址,从而避免了DNS查询。
AI_NUMERICSERV 在hints参数中设置,强制service参数使用十进制端口号的字符串形式,而不能是服务名
AI_V4MAPPED 在hints参数中设置。如果ai_family被设置为AF_INET6,那么当没有满足条件的IPv6地址被找到时,将IPv4地址映射为IPv6地址
AI_ALL 必须和AI_V4MAPPED同时使用,否则将被忽略。表示同时返回符合条件的IPv6地址以及由IPv4地址映射得到的IPv6地址
AI_ADDRCONFIG 仅当至少配置有一个IPv4地址(除了回路地址)时,才返回IPv4地址信息;同样,仅当至少配置有一个IPv6地址(除了回路地址)时,才返回IPv6地址信息。它和AI_V4MAPPED是互斥的

3.4 getnameinfo

#include <sys/socket.h>
#include <netdb.h>

/*
 * getnameinfo: the inverse of getaddrinfo(3): it converts a socket address 
 * 		to a corresponding host and service, in a protocol-independent manner.
 * 		【通过socket地址同时获得以字符串表示的主机名和服务名】
 * @params:
 * 	addr: 地址
 * 	addrlen: 	地址长度
 * 	host: 		保存返回的主机名
 * 	hostlen: 	主机名长度
 * 	serv: 		保存返回的服务名
 * 	servlen: 	服务名长度
 * 	flags: 		见表 flags参数
 * @return:
 * 	同getaddrinfo()
 */
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
                char *host, socklen_t hostlen,
                char *serv, socklen_t servlen, int flags);
表 flags参数
选项 含义
NI_NAMEREQD 如果通过socket地址不能获得主机名,则返回一个错误
NI_DGRAM 返回数据包服务。大部分同时支持流和数据报的服务使用相同的端口号来提供这两种服务。但端口号512~514是例外。比如TCP的514端口提供的是shell登录服务,而UDP的514端口提供的是syslog服务(/etc/services)
NI_NUMERICHOST 返回字符串表示的IP地址,而不是主机名
NI_NUMERICSERV 返回字符串表示的十进制端口号,而不是服务名
NI_NOFQDN 仅返回主机域名的第一部分。比如对主机名nebula.testing.com,getaddrinfo只将nebula写入host缓存中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章