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緩存中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章