45. connect 實現定時


我們需要先連接 getaddrinfo, 而 getaddrinfo 的特性實在太多, 本節也僅僅只是簡單的做一個小結, 如果想要了解更加全面可以參考 unp 第245頁.


getaddrinfo 函數

getaddrinfo 函數是與協議無關 (TCP, UDP都可以) 並且不僅支持 IPV4 還支持 IPV6 .

函數原型 :

#include <netdb.h>

int getaddrinfo(const char *hostname, const char *service, 
					const struct addrinfo *hints, struct addrinfo **result);

int gai_strerror(int error);

void freeaddrinfo(struct addrinfo *);
struct addrinfo{
	int	ai_flags;
    int	ai_family;		// AF_XXXX
    int ai_socktype;	// SOCK_XXXX
    int	ai_protocol;	
    socklen_t	ai_addrlen;	// sizeof(ai_addr)
    char *	ai_cannoname;
    struct sockaddr *	ai_addr;
    struct addrinfo *	ai_next; 
};

成功 : 返回0

失敗 : 返回非 0 並設置錯誤碼由gai_strerror()輸出

參數 :

  • hostname : 主機名 (或者地址串)
  • service : 服務名 (或者IP地址)
  • hints : 返回的參數 (可以爲 NULL)
    • ai_flags : AI_CANONNAME(返回主機規範名字), **AI_PASSIVE(服務端被動監聽)**等.
    • ai_family : 可以設置返回的套接字類型
  • result : 服務有多個套接字類型(鏈表)

因爲 getaddrinfo 內部會申請內存, 所以最後需要調用 freeaddrinfo 函數釋放內存.


host_serv 函數

通過封裝 getaddrinfo 函數保證用戶不用分配和釋放 addrinfo 內存.

完整代碼 : host_serv.c

#include "client_web.h"

// 返回給定主機名(或地址串) 的基本信息
struct addrinfo* host_serv(const char *hostname, const char *service, 
								int family, int socktype){
	int n;
	struct addrinfo hint, *res;

	bzero(&hint, sizeof(hint));	// 書中並沒有清零, 這步不能省略
	hint.ai_flags = AI_CANONNAME;
	hint.ai_family = family;
	hint.ai_socktype = socktype;
	if((n = getaddrinfo(hostname, service, &hint, &res)) != 0){
		gai_strerror(n);
		return NULL;
	}

	return res;
}

tcp_connect 函數

接下來我們通過 getaddrinfo 函數來封裝 connect 函數, 保證能與域名的其中一個地址建立連接.

完整代碼 : tcp_connect.c

#include "tcp_connect.h"

// 遍歷主機的所有 IP 地址, 直到建立一個 TCP 連接 
int tcp_connect(const char *hostname, const char *service){
	int sockfd, n;
	struct addrinfo hint, *res, *resave;

	bzero(&hint, sizeof(hint));
	hint.ai_family = AF_INET;
	hint.ai_socktype = SOCK_STREAM;
	
	if((n = getaddrinfo(hostname, service, &hint, &res)) != 0){
		gai_strerror(n);
		exit(1);
	}

	resave = res;
	// 直到建立連接 或 遍歷結束退出
	do{
		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if(socket < 0)
			continue;
		
		if(connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
			break;
		close(sockfd);
	}while((res	= res->ai_next) != NULL);
	if(res == NULL){
		fprintf(stderr, "tcp_connect error for %s, %s", hostname, service);
		exit(1);
	}

	freeaddrinfo(resave);
	
	return sockfd;
}

小結

本節只是簡單的介紹了 getaddrinfo 函數, 並且封裝了兩個函數準備之後完成 web客戶程序 做準備.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章