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

C语言SSH客户端二次send()数据包未被服务器接收问题排查

SSH客户端二次send()数据包无法送达问题

问题描述

用C语言开发SSH客户端,当前可正常发送SSH协议版本并接收服务器返回的版本信息,但发送密钥交换初始化(kex init)数据包时,服务器完全接收不到该数据包(已通过Wireshark验证)。客户端能正常接收服务器的kex init消息;若调换发送顺序(先发kex包再发协议包),服务器仅能收到格式错误的kex包,第二个包仍无法被接收。

优先解决数据包送达问题,暂不纠结内容格式,相关代码如下:

初始化TCP连接

int start_client(const char *host, const int port) {
    struct sockaddr_in address;
    int sock = 0;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    address.sin_family = AF_INET;
    address.sin_port = htons(22); 

    inet_pton(AF_INET, host, &address.sin_addr);

    int output = connect(sock, (struct sockaddr *)&address, sizeof(address));
    
    if (output == 0) {
        printf("successful connection\n");
    } else {
        printf("no connection\n");
    }

    sendProtocol(sock);

    sendKexInit(sock);

    close(sock);

    return 0;
}

发送协议数据包

int sendProtocol(int sock) {
    unsigned char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);  // Clear the buffer

    char *protocol = "SSH-2.0-mySSH\r\n";
    int sentBytes = send(sock, protocol, strlen(protocol), 0);

    if (sentBytes != -1) {
        printf("Successful protocol send! Number of protocol bytes sent: %i\n", sentBytes);
    } else {
        printf("Send did not complete successfully.\n");
    }
    
    ssize_t bytesRecieved = recv(sock, buffer, BUFFER_SIZE, 0);
    
    if (bytesRecieved > 0) {
        printf("server protocol: %s", buffer);
    } else {
        printf("No server protocol recieved :(\n");
    }

    return 0;
}

发送(格式错误的)kex数据包

int sendKexInit (int sock) {
    unsigned char buffer[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);  // Clear the buffer

    unsigned char *cookie = generateRandomBytes(16);

    // // this is hardcoded, just trying to get it to work
    uint32_t packetLen = 24;
    packetLen = htonl(packetLen); // fix endian-ness
    memcpy(buffer, &packetLen, sizeof(packetLen)); 
    buffer[4] = 6;

    buffer[5] = SSH_MSG_KEXINIT;
    memcpy(buffer + 6, cookie, 16); 
    free(cookie);
    memset(buffer + 22, 0, 2);
    
    int sentBytes = send(sock, buffer, 24, 0);
    if (sentBytes != -1) {
        printf("Successful kex send! Number of kex bytes sent: %i\n", sentBytes);
    } else {
        printf("Send did not complete successfully.\n");
    }
    
    memset(buffer, 0, BUFFER_SIZE);  // Clear the buffer
    ssize_t bytes_recieved = recv(sock, buffer, BUFFER_SIZE, 0);
    
    if (bytes_recieved > 0) {
        printf("response: %s\n", buffer);
    } else {
        printf("No server response recieved :(\n");
    }

    // printing out some of the response (buffer is too small for all of it)
    // for (int i = 0; i < sizeof(buffer); i++) {
    //     printf("%02x ", buffer[i]);
    // }
    // printf("\n");

    return 0;
}

疑问

send()返回值显示发送字节数正确,但始终只有第一个发送的数据包能被服务器接收,第二个无法送达。猜测可能是第一个send()未正确收尾,但不确定问题所在,请问是否有明显遗漏的点导致二次send()的数据包无法送达?


排查关键点

结合代码和现象,有几个需要优先验证的点:

  1. 禁用Nagle算法
    TCP默认启用的Nagle算法会延迟小数据包发送,等待前一个数据包的ACK后再发送后续小数据包。虽然第一个包的ACK应该在sendProtocol的recv阶段已收到,但仍可能存在延迟。在创建socket后添加以下代码禁用Nagle算法:
int opt = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
  1. 检查sendKexInit中的阻塞问题
    sendKexInit在send后立即调用recv,如果服务器未收到kex包(或包格式错误),不会返回响应,导致recv一直阻塞,程序无法走到close(sock)。此时内核可能不会强制发送缓冲区中的数据,Wireshark自然抓不到包。可以先注释掉sendKexInit中的recv代码,仅保留send逻辑,再用Wireshark验证是否能发送第二个包。

  2. 确认BUFFER_SIZE足够且无内存溢出
    如果BUFFER_SIZE小于24,sendKexInit中构造数据包时会发生栈内存溢出,破坏socket相关结构,导致后续send操作异常。确保BUFFER_SIZE定义大于等于24。

  3. 检查generateRandomBytes函数的正确性
    如果generateRandomBytes返回的内存存在越界或未正确初始化,可能会破坏栈上的socket描述符或其他关键数据,导致send失败。可以临时替换为固定的16字节测试数据,排除该函数的影响:

unsigned char cookie[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                            0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
  1. 确保TCP连接状态正常
    在sendKexInit调用前,添加代码检查socket的连接状态,避免连接已意外断开:
int error = 0;
socklen_t len = sizeof(error);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
if (error != 0) {
    printf("Socket error: %d\n", error);
    return -1;
}

内容的提问来源于stack exchange,提问作者Ruben Boero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:07:03