C语言Socket服务器客户端IP扫描:端口扫描无显示问题求助
问题分析与修复方案
看起来你在端口扫描模块犯了几个典型的Socket使用错误,这就是为什么服务器端看不到开放端口的原因。让我帮你拆解问题并一步步修复:
核心错误点
- 误用服务器监听Socket发起连接:你的
sockfd是用来监听客户端连接的服务器Socket,它的职责是等待外部连接,根本不能用来调用connect()发起端口扫描请求。必须为每个端口扫描创建独立的Socket实例。 - 目标IP未正确配置:你在端口扫描时依然沿用了服务器的
INADDR_ANY地址,相当于在扫描服务器自身,而不是你输入的目标主机或客户端IP。 - 输入处理存在BUG:
gets()函数不安全,且scanf()读取端口后会留下换行符,导致gets()直接读取空内容。 - 缺少必要头文件:使用
strerror()需要包含<errno.h>,否则会编译报错或行为异常。
修复后的完整代码
#include <netdb.h> #include <stdio.h> #include <netinet/in.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <errno.h> // 新增:用于错误信息输出 #include <unistd.h> // 新增:用于关闭Socket #define MAX 80 #define PORT 8080 #define SA struct sockaddr int main() { int sockfd, connfd, len, start, end, err; struct sockaddr_in servaddr, cli, target_addr; // 新增target_addr存储扫描目标 char ips[2][20]; char hostname[30]; struct hostent *host_info; // 新增:用于解析主机名为IP // 服务器Socket创建与初始化 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("socket creation failed...\n"); exit(0); } printf("Socket successfully created..\n"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { printf("socket bind failed...\n"); exit(0); } printf("Socket successfully binded..\n"); if ((listen(sockfd, 5)) != 0) { printf("Listen failed...\n"); exit(0); } printf("Server listening..\n"); len = sizeof(cli); // 接受2个客户端连接并保存IP for(int i=0; i<2; i++){ connfd = accept(sockfd, (SA*)&cli, &len); if (connfd < 0) { printf("server accept failed...\n"); exit(0); } printf("server accept the client... IP: %s\n", inet_ntoa(cli.sin_addr)); strcpy(ips[i], inet_ntoa(cli.sin_addr)); close(connfd); // 新增:接受后关闭客户端连接,避免资源泄漏 } // 显示已连接的客户端IP printf("\nConnected client IPs:\n"); for(int i=0; i<2; i++){ printf("- %s\n", ips[i]); } // 端口扫描模块修复 printf("\n=== Port Scan Module ===\n"); // 替换gets为fgets,处理输入缓冲区残留的换行符 printf("Enter hostname or IP : "); fflush(stdout); // 确保提示符正常输出 fgets(hostname, sizeof(hostname), stdin); // 去掉fgets读取的末尾换行符 hostname[strcspn(hostname, "\n")] = '\0'; // 解析目标IP(支持主机名或直接IP) if ((host_info = gethostbyname(hostname)) == NULL) { // 如果不是主机名,尝试直接解析IP地址 if (inet_pton(AF_INET, hostname, &target_addr.sin_addr) <= 0) { printf("Invalid hostname/IP address\n"); close(sockfd); exit(1); } } else { memcpy(&target_addr.sin_addr, host_info->h_addr, host_info->h_length); } target_addr.sin_family = AF_INET; // 获取起始端口并做合法性校验 printf("Enter start port number : "); while (scanf("%d", &start) != 1 || start < 1 || start > 65535) { printf("Invalid port, enter a number between 1-65535: "); // 清空输入缓冲区的无效内容 while (getchar() != '\n'); } // 获取结束端口并做合法性校验 printf("Enter end port number : "); while (scanf("%d", &end) != 1 || end < start || end > 65535) { printf("Invalid port, enter a number >= %d and <=65535: ", start); while (getchar() != '\n'); } printf("\nStarting port scan on %s (%s)...\n", hostname, inet_ntoa(target_addr.sin_addr)); for (int j = start; j <= end; j++) { // 为每个端口扫描创建独立的Socket int scan_sock = socket(AF_INET, SOCK_STREAM, 0); if (scan_sock < 0) { printf("Failed to create scan socket for port %d\n", j); continue; } target_addr.sin_port = htons(j); // 设置连接超时(避免因阻塞导致扫描过慢) struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; setsockopt(scan_sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); setsockopt(scan_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); err = connect(scan_sock, (SA*)&target_addr, sizeof(target_addr)); if (err == 0) { printf("Port %-5d is open\n", j); } // 关闭当前扫描用的Socket,释放资源 close(scan_sock); } // 关闭服务器监听Socket close(sockfd); return 0; }
关键修复说明
- 独立扫描Socket:每次端口扫描都创建新的Socket,用完立即关闭,既不影响服务器的监听功能,也避免资源泄漏。
- 正确配置目标IP:新增
target_addr结构体专门存储扫描目标,支持解析主机名或直接IP地址,确保扫描的是你指定的主机。 - 输入逻辑优化:用安全的
fgets()替代gets(),处理scanf()后的换行残留,添加端口范围合法性校验(1-65535)。 - 连接超时设置:通过
setsockopt()设置1秒连接超时,避免因某些端口阻塞导致扫描速度极慢。 - 资源清理:接受客户端连接后及时关闭
connfd,扫描完成后关闭所有临时Socket,避免文件描述符耗尽。
额外建议
- 如果要扫描之前保存的客户端IP(
ips数组),可以直接将ips[i]传入解析逻辑,无需手动输入。 - 单线程扫描速度较慢,可尝试用多线程/多进程优化扫描效率。
- 扫描1-1024的特权端口需要root权限,普通用户会连接失败。
内容的提问来源于stack exchange,提问作者Salim Poui
相关产品推荐
相关产品推荐

