You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修复说明

  1. 独立扫描Socket:每次端口扫描都创建新的Socket,用完立即关闭,既不影响服务器的监听功能,也避免资源泄漏。
  2. 正确配置目标IP:新增target_addr结构体专门存储扫描目标,支持解析主机名或直接IP地址,确保扫描的是你指定的主机。
  3. 输入逻辑优化:用安全的fgets()替代gets(),处理scanf()后的换行残留,添加端口范围合法性校验(1-65535)。
  4. 连接超时设置:通过setsockopt()设置1秒连接超时,避免因某些端口阻塞导致扫描速度极慢。
  5. 资源清理:接受客户端连接后及时关闭connfd,扫描完成后关闭所有临时Socket,避免文件描述符耗尽。

额外建议

  • 如果要扫描之前保存的客户端IP(ips数组),可以直接将ips[i]传入解析逻辑,无需手动输入。
  • 单线程扫描速度较慢,可尝试用多线程/多进程优化扫描效率。
  • 扫描1-1024的特权端口需要root权限,普通用户会连接失败。

内容的提问来源于stack exchange,提问作者Salim Poui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:04:13