in_addr 转换为 string(char*)

本文介绍了一个将IPv4地址从in_addr结构体转换为字符型点分十进制表示的C语言函数。通过位操作和内存分配,该函数实现了IP地址的有效转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/* * @copyright: copyright (c) 2025 Chengdu TP-Link Technologies Co.Ltd. * @fileName: server.h * @description: function declaration file of server.c * @Author: Wang ZHiheng * @email: wangzhiheng@tp-link.com.hk * @version: 1.0.0 * @Date: 2025-08-07 * @history: \arg 1.0.0, 25Aug07, Wang Zhiheng, Create the file */ #include <arpa/inet.h> #include <sys/epoll.h> #include <pthread.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <sys/sendfile.h> #include <dirent.h> #include <stdlib.h> #include <pthread.h> #include <ctype.h> #include <fcntl.h> #include <math.h> #include "server.h" /* 函数声明文件 */ /* 错误处理 */ #define handle_error(cmd, result) \ if (result < 0) \ { \ perror(cmd); \ return -1; \ } /** * @function: int init_listen_fd(unsigned short port) * @description: 新建一个用于TCP监听的socket文件描述符,并返回 * @return 返回监听socket的文件描述符 * @note: * @param {unsigned short} port 监听端口号 */ int init_listen_fd(unsigned short port) { int listen_sockfd; /* 监听socket */ struct sockaddr_in listen_addr; /* 监听地址 */ memset(&listen_addr, 0, sizeof(listen_addr)); /* 初始化 */ int temp_result; /* 存储临时变量用于debug */ /* 创建监听socket */ listen_sockfd = socket(AF_INET, SOCK_STREAM, 0); handle_error("socket", listen_sockfd); /* 设置端口复用 */ int opt = -1; int ret = setsockopt(listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); handle_error("setsockopt", listen_sockfd); /* 绑定端口和IP */ listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = INADDR_ANY; listen_addr.sin_port = htons(port); /* 绑定地址 */ temp_result = bind(listen_sockfd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)); handle_error("bind", temp_result); printf("bind success\n"); /* 进入监听模式 */ if (listen(listen_sockfd, 128) < 0) { perror("listen failed"); close(listen_sockfd); exit(EXIT_FAILURE); } printf("listen success\n"); return listen_sockfd; } /** * @function: int epoll_run(int listen_sockfd) * @description: 启动epoll * @return 返回值无实际作用,因为会处于循环状态监听 * @note: * @param {int} listen_sockfd 监听socket的文件描述符 */ int threads_run(int listen_sockfd) { int temp_result; /* 存储临时变量用于debug */ struct sockaddr_in client_addr; /* 用于存储客户端地址 */ memset(&client_addr, 0, sizeof(client_addr)); /* 接收client连接 */ socklen_t client_addr_len = sizeof(client_addr); while (1) { pthread_t pid; /* 子线程, 用于从客户端读取数据*/ int *client_fd_ptr = (int *)malloc(sizeof(int)); int client_fd = accept(listen_sockfd, (struct sockaddr *)&client_addr, &client_addr_len); *client_fd_ptr = client_fd; if (client_fd < 0) { perror("accept failed"); close(listen_sockfd); exit(EXIT_FAILURE); } printf("a client from ip:%s at port %d, socket %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd); /* 调用子线程接收客户端数据, 并发送响应*/ if ((pthread_create(&pid, NULL, recv_http_request, client_fd_ptr)) != 0) { // perror("pthread_create"); // pthread_exit(&pid); perror("pthread_create failed"); close(client_fd); free(client_fd_ptr); continue; } /* 使子线程处于detached状态,使其终止时自动回收资源,同时不阻塞线程 */ // pthread_join(pid, NULL); pthread_detach(pid); } printf("close socket"); close(listen_sockfd); return 0; } /** * @function: void* recv_http_request(void* arg) * @description: 接收http请求, 读取数据 * @return * @note: conn_fd表示连接socket, epoll_fd表示epoll树 * @param {void*} arg */ void *recv_http_request(void *arg) { printf("进入recv\n"); int client_fd = *(int *)arg; int len = 0; /* 用于接收recv的返回值 */ char buf[4096] = {0}; /* 接收客户端请求数据 */ len = recv(client_fd, buf, sizeof(buf), 0); if (len <= 0) { printf("Client disconnected\n"); close(client_fd); free(arg); return NULL; } printf("len%d\n", len); parse_request(buf, (int *)arg); free(arg); printf("退出 recv\n"); close(client_fd); } /** * @function: int parse_request(const char *line, int conn_fd) * @description: 解析HTTP的请求行,并调用send_http_response * @return {*} * @note: * @param {char} *line * @param {int} conn_fd */ int parse_request(const char *message, int *conn_fd) { /* 解析请求行 */ char method[12]; /* 存储客户端请求方法,如GET/POST */ char path[1024]; char *filename = NULL; /* 处理客户端请求的静态资源(目录或文件) */ char version[1024]; printf("获取方法和路径\n"); sscanf(message, "%s %s %s", method, path, version); printf("请求内容: \nmethod:%s, path:%s\n\n", method, path); /* 不区分大小写的比较,如果不等于0,则返回-1 */ if (strcasecmp(method, "get") == 0) { /* 处理客户端请求的静态资源(目录或文件),因为当前获得的/xxx/1.jpg是相对于工作路径的,所以需要转换为./xxx/1.jpg或者xxx/1.jpg */ if (strcmp(path, "/") == 0) { /* 如果get当前的工作目录 */ filename = "./"; } else { filename = path + 1; /* get工作目录中的一个资源 */ } send_http_response(*conn_fd, filename); } else if(strcasecmp(method, "post") == 0) { // 检查是否为前端指定的 /data/contact.json 路径 if (strcmp(path, "/data/contact.json") == 0) { // 查找请求体起始位置 const char *body_start = strstr(message, "\r\n\r\n"); if (body_start) { body_start += 4; // 跳过空行 // 解析表单数据 (application/x-www-form-urlencoded) char *name = NULL, *email = NULL, *message_content = NULL; char *token = strtok((char *)body_start, "&"); while (token != NULL) { if (strstr(token, "name=") == token) { name = token + 5; // 跳过 "name=" } else if (strstr(token, "email=") == token) { email = token + 6; // 跳过 "email=" } else if (strstr(token, "message=") == token) { message_content = token + 8; // 跳过 "message=" } token = strtok(NULL, "&"); } // 打印解码后的表单数据 printf("收到联系表单数据:\n"); printf("姓名: %s\n", url_decode(name)); printf("邮箱: %s\n", url_decode(email)); printf("留言: %s\n", url_decode(message_content)); // 发送前端期望的JSON响应 const char *response = "HTTP/1.1 200 OK\r\n" "Content-Type: application/json\r\n" "Content-Length: 27\r\n" "Connection: close\r\n" "\r\n" "{\"callback\":\"提交成功\"}"; send(*conn_fd, response, strlen(response), 0); } else { // 没有找到请求体 const char *response = "HTTP/1.1 400 Bad Request\r\n" "Content-Type: application/json\r\n" "Content-Length: 39\r\n" "Connection: close\r\n" "\r\n" "{\"error\":\"请求体缺失\"}"; send(*conn_fd, response, strlen(response), 0); } } } else { return -1; } return 0; } /** * @function: void send_http_response(int sockfd, const char* file_path) * @description: 发送http响应报文, 包括 响应头 响应文件数据 等 * @return * @note: * @param {int*} sockfd 与客户端连接的socket文件描述符 * @param {char*} file_path 需要发送的文件路径 */ void send_http_response(int sockfd, const char *file_path) { struct stat file_stat; /* stat函数的传出采纳数 */ char buffer[(int)pow(2, 17)]; /* 用于读取文件和发送数据的缓冲区 */ ssize_t bytes_read; /* 用于接收read返回的数据长度 */ if (stat(file_path, &file_stat) != 0) { /* 文件不存在或无法获取状态 */ const char *response_404 = "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html\r\n" "Content-Length: 133\r\n" "Connection: close\r\n" "\r\n" "<html><body><h1>404 Not Found</h1><p>The requested file was not found on this server.</p></body></html>"; send(sockfd, response_404, strlen(response_404), 0); close(sockfd); return; } int fd = open(file_path, O_RDONLY); if (fd == -1) { /* 打开文件失败 */ const char *response_500 = "HTTP/1.1 500 Internal Server Error\r\n" "Content-Type: text/html\r\n" "Content-Length: 151\r\n" "Connection: close\r\n" "\r\n" "<html><body><h1>500 Internal Server Error</h1><p>Failed to open the requested file.</p></body></html>"; send(sockfd, response_500, strlen(response_500), 0); close(sockfd); return; } printf("打开文件%s\n", file_path); const char *mime_type = get_mime_type(file_path); /* 发送HTTP响应头 */ char response_header[2048]; snprintf(response_header, sizeof(response_header), "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "Connection: keep-alive\r\n" "\r\n", mime_type, file_stat.st_size); send(sockfd, response_header, strlen(response_header), 0); /* 循环读取并发送文件内容 */ while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { ssize_t sent = send(sockfd, buffer, bytes_read, 0); if (sent < 0) { perror("send failed"); break; } } printf("bytes_read: %zd\n", bytes_read); close(fd); /* 关闭文件描述符 */ } /** * @function: char *url_decode(const char *src) * @description: 将post请求解码为姓名、邮箱、留言三项 * @return * @note: * @param {int*} sockfd 与客户端连接的socket文件描述符 * @param {char*} file_path 需要发送的文件路径 */ char *url_decode(const char *src) { if (!src) return NULL; size_t src_len = strlen(src); char *decoded = malloc(src_len + 1); char *dst = decoded; while (*src) { if (*src == '+') { *dst++ = ' '; src++; } else if (*src == '%' && isxdigit(src[1]) && isxdigit(src[2])) { char hex[3] = {src[1], src[2], '\0'}; *dst++ = (char)strtol(hex, NULL, 16); src += 3; } else { *dst++ = *src++; } } *dst = '\0'; return decoded; } /** * @function: const char* get_mime_type(const char* filename) * @description: MIME类型映射, 获取发送给客户端文件名称的后缀名, 以生成对应Content-Type参数 * @return 返回存储Content-Type参数的指针 * @note: 根据提供的web页面, 只涉及了其中三种Content-Type参数 * @param {char*} filename 要发送给客户端的路径名或文件名 */ const char *get_mime_type(const char *filename) { const char *dot = NULL; /* 接收strrchr返回参数,存储文件后缀名 */ dot = strrchr(filename, '.'); /* strrchr获取指定字符的最后一次出现 */ /* 根据文件后缀名生成对应Content-Type参数 */ if (dot == NULL) return "application/octet-stream"; if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0) return "text/html"; else if (strcmp(dot, ".css") == 0) return "text/css"; else if (strcmp(dot, ".png") == 0 || strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0) return "image/jpeg"; /* 注意:这里为了简化,JPEG和PNG都使用了image/jpeg,实际应分开处理 */ return "application/octet-stream"; } 没有问题了,不改代码,就统一一下格式,将对Post和get的处理分别封装为一个函数,完善函数注释,注释统一为/**/格式,记住,不要修改代码逻辑
最新发布
08-09
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int server_fd, client_fd; struct sockaddr_in6 server_addr, client_addr; socklen_t client_len = sizeof(client_addr); int addr_len = sizeof(server_addr); char buffer[BUFFER_SIZE] = {0}; const char *server_msg = "From Server"; /*创建socket描述符*/ server_fd = socket(AF_INET6, SOCK_STREAM, 0); /*协议域, socket类型, 指定协议*/ if (server_fd < 0) { perror("socket establish failed:"); exit(EXIT_FAILURE); } printf("socket success \n\n"); /*绑定socked和端口*/ memset(&server_addr, 0, sizeof(server_addr)); /*清零,避免初始化时错误赋值*/ server_addr.sin6_family = AF_INET6; /*地址族,ipv6为AF_INET6*/ server_addr.sin6_addr = in6addr_any; /*监听所有ipv6地址*/ server_addr.sin6_port = htons(PORT); /*监听的端口号,为大端*/ if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } printf("bind success \n\n"); /*监听端口*/ if (listen(server_fd, 5) < 0) /*cocket描述, 最大连接数*/ { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("listen success\n\n"); int count = 1; while (count <= 10) { /*返回新的socket(client_fd)与客户端通讯*/ /*原套接字则继续监听*/ client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { perror ("accept failed"); continue; } /*打印客户端信息*/ char client_ip[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &client_addr.sin6_addr, client_ip, INET6_ADDRSTRLEN); printf("a client from --> ip:%s, port:%d, socket:%d \n\n", client_ip, ntohs(client_addr.sin6_port), client_fd); /*向客户端发送信息*/ ssize_t sent_bytes = send(client_fd, server_msg, strlen(server_msg), 0); if (sent_bytes < 0) { perror("send failed"); } else { printf("message %s send success, %ld bytes \n\n", server_msg, sent_bytes); } /*接收客户端信息*/ ssize_t recv_bytes = recv(client_fd, buffer, BUFFER_SIZE, 0); if (recv_bytes < 0) { perror("recv failed"); } else { buffer[recv_bytes] = '\0'; printf("recv success --> message:%s, %ld bytes\n\n", buffer, recv_bytes); } count++; /*关闭客户端套接字*/ close(client_fd); } close(server_fd); return 0; }
08-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值