[socket] server-side function - sock_daemon_connect

int sock_daemon_connect(
	int port)
{
	struct addrinfo *res, *t;
	struct addrinfo hints = {
		.ai_flags    = AI_PASSIVE,
		.ai_family   = AF_UNSPEC,
		.ai_socktype = SOCK_STREAM
	};
	char *service;
	int n;
	int sockfd = -1, connfd;

	if (asprintf(&service, "%d", port) < 0) {
		fprintf(stderr, "asprintf failed\n");
		return -1;
	}

	n = getaddrinfo(NULL, service, &hints, &res);
	if (n < 0) {
		fprintf(stderr, "%s for port %d\n", gai_strerror(n), port);
		free(service);
		return -1;
	}

	for (t = res; t; t = t->ai_next) {
		sockfd = socket(t->ai_family, t->ai_socktype, t->ai_protocol);
		if (sockfd >= 0) {
			n = 1;

			setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof n);

			if (!bind(sockfd, t->ai_addr, t->ai_addrlen))
				break;
			close(sockfd);
			sockfd = -1;
		}
	}

	freeaddrinfo(res);
	free(service);

	if (sockfd < 0) {
		fprintf(stderr, "couldn't listen to port %d\n", port);
		return -1;
	}

	listen(sockfd, 1);
	connfd = accept(sockfd, NULL, 0);
	close(sockfd);
	if (connfd < 0) {
		fprintf(stderr, "accept() failed\n");
		return -1;
	}

	return connfd;
}
这段代码是一个用于作为守护进程监听端口并接受连接的函数,它接受一个参数:服务端口。函数的返回值为接受到的客户端连接文件描述符,如果监听失败则返回 -1。下面是对这段代码的中文分析:
1. 定义一个结构体指针 res,用于存储服务器地址信息;定义一个结构体指针 t,用于遍历地址信息;定义一个名为 hints 的结构体,用于存储地址解析时的家族、协议和套接字类型等信息。其中,AI_PASSIVE 表示使用被动模式,即监听模式。
2. 定义一个字符指针 service,用于存储端口号的字符串表示;定义一个整数变量 n,用于存储地址解析的结果;定义一个整数变量 sockfd,用于存储客户端套接字文件描述符。定义一个整数变量 connfd,用于存储接受到的客户端连接文件描述符。
3. 使用 asprintf 函数将端口号格式化为字符串,并将其存储在 service 指针指向的内存中。如果格式化失败,函数将返回 -1,并输出错误信息。
4. 使用 getaddrinfo 函数根据服务器名称和服务端口获取地址信息。这里将服务器名称设置为 NULL,表示使用默认的服务名称。如果获取地址信息失败,函数将返回 -1,并输出错误信息。同时释放 service 指针指向的内存。
5. 使用一个 for 循环遍历地址信息。在循环中,为每个地址创建一个套接字,并尝试绑定套接字到特定的端口。如果绑定成功,跳出循环;如果绑定失败,关闭套接字并重置 sockfd 为 -1。
6. 释放地址信息,释放 service 指针指向的内存。如果 sockfd 为 -1,说明监听失败,输出错误信息。
7. 将套接字设置为监听模式,并等待客户端的连接。如果接受到客户端连接,函数将返回接受到的客户端连接文件描述符;如果监听失败,返回值为 -1。
8. 关闭套接字。
9. 如果 connfd 小于 0,说明接受客户端连接失败,输出错误信息并返回 -1。
10. 返回接受到的客户端连接文件描述符。
这是一个用C语言编写的函数,名为sock_daemon_connect。这个函数的目标是在服务器端建立一个与指定端口的连接。

函数首先定义了一个addrinfo结构体的指针变量res和一个临时变量t。然后,它定义了一个addrinfo结构体hints,其中设置了地址族为AF_UNSPEC(可以是IPv4或IPv6),套接字类型为SOCK_STREAM(TCP流套接字),并设置了AI_PASSIVE标志,这表示该套接字将用于被动模式(等待连接)。

函数声明了两个整数变量n和两个套接字变量sockfd和connfd。然后,它使用asprintf函数将端口号转换为字符串,如果失败则打印错误信息并返回-1。

接下来,函数调用getaddrinfo函数,将NULL、端口号和服务器的地址信息作为参数,以获取地址信息。如果失败,则打印错误信息并释放之前分配的内存,然后返回-1。

然后,函数遍历所有的地址信息。对于每一个地址,创建一个套接字,然后尝试绑定到该地址。如果绑定成功,则设置套接字的选项以允许地址重用。然后跳出循环并继续执行。如果绑定失败,则关闭该套接字并继续尝试下一个地址。

如果所有的地址都尝试过但绑定仍然失败,则释放地址信息的内存和端口号字符串的内存,然后打印错误信息并返回-1。

如果绑定成功,则使该套接字变为被动模式,并等待客户端的连接。一旦有客户端连接,就使用accept函数接受连接,然后关闭服务器端的套接字。最后,返回客户端的套接字文件描述符。

如果接受连接失败,则关闭服务器端的套接字,释放地址信息的内存和端口号字符串的内存,然后打印错误信息并返回-1。

总的来说,这个函数的功能是创建一个套接字并将其绑定到指定的端口,然后等待客户端的连接。一旦有客户端连接,就接受连接并返回客户端的套接字文件描述符。
该函数首先使用getaddrinfo获取一个包含所有可用端口的地址,然后为每个可用端口创建一个套接字。如果成功绑定到某个端口并监听连接,则返回该端口的套接字描述符。否则,它将尝试再次绑定到下一个可用端口,直到所有端口都被检查过为止。最后,它接受并关闭监听套接字,并返回由accept调用返回的新连接套接字描述符。

注意:这个函数没有处理超时或错误,例如网络错误或端口不可用。在实际应用中,你可能需要添加一些错误处理和超时逻辑。
这段代码是一个名为 `sock_daemon_connect` 的函数,用于在守护进程中建立与客户端的连接。

函数接受一个参数 `port`,表示要监听的端口号。

函数首先定义了一个 `addrinfo` 结构体指针数组 `res` 和 `t`,用于存储服务器的地址信息。然后定义了一个 `hints` 结构体,指定了地址族为 `AF_UNSPEC`(任意地址族)和套接字类型为流式套接字(SOCK_STREAM)。

接下来,函数使用 `asprintf` 函数将端口号转换为字符串,并将其存储在 `service` 变量中。如果转换失败,则打印错误消息并返回 -1。

然后,函数调用 `getaddrinfo` 函数,传入空指针、服务名称、地址信息提示和地址信息指针数组,以获取服务器的地址信息。如果获取失败,则打印错误消息,释放 `service` 变量并返回 -1。

接下来,函数遍历地址信息链表,依次创建套接字并尝试绑定到指定的地址和端口上。如果绑定成功,则跳出循环;否则关闭套接字并继续尝试下一个地址。最后,释放地址信息链表和 `service` 变量。

如果最终无法监听到指定的端口,则打印错误消息并返回 -1。

如果监听成功,则调用 `listen` 函数开始监听连接请求。然后调用 `accept` 函数接受客户端的连接请求,并返回连接的文件描述符。最后,关闭监听套接字并返回连接的文件描述符。

注意:这段代码使用了 `getaddrinfo`、`asprintf`、`socket`、`bind`、`close`、`listen` 和 `accept` 等函数,需要在代码中包含相应的头文件(例如 `<sys/types.h>`、`<sys/socket.h>`、`<netdb.h>` 等)。

Guess you like

Origin blog.csdn.net/eidolon_foot/article/details/132623045