我不明白为什么 recv 没有保存到缓冲区

I can't figure out why recv isn't saving to a buffer

我正在创建一个 TCP 客户端,它可以将消息发送到服务器(使用 winsock2),然后服务器将消息发送到每个其他套接字,但由于某种原因,它的 recv() 没有获取数据这是自己的消息。

#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <time.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")

DWORD WINAPI receive(LPVOID s) {
    char output[4096];
    for(;;) {
        recv((SOCKET) s, output, sizeof(output), 0);
        printf("%s", output);
    }
    return 0;
}

int main() {
    WSADATA wsa;
    SOCKET s;
    struct sockaddr_in server;
    clock_t start;
    double finished;

    start = clock();
    printf("initializing...\n");
    if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("failed: %d", WSAGetLastError());
        //sleep(3);
        return 1;
    }
    
    finished = (double) (clock() - start) / CLOCKS_PER_SEC;
    printf("initialized %fs\n", finished);
    
    start = clock();
    printf("creating socket...\n");
    if((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        printf("socket creation failed: %d", WSAGetLastError());
        //sleep(3);
        return 1;
    }

    finished = (double) (clock() - start) / CLOCKS_PER_SEC;
    printf("socket created %fs\n", finished);
    
    printf("what ip would you like to connect to: ");
    char ip[64];
    scanf("%s", ip);
    server.sin_addr.s_addr = inet_addr(ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(8080);

    if(connect(s, (struct sockaddr*) &server, sizeof(server)) < 0) {
        printf("connect error\n");
        return 1;
    }
    
    printf("connection to %s was successful\n", ip);

    char username[256];
    printf("what username would you like to use: ");
    scanf(" %[^\n]s", &username);

    HANDLE handle = CreateThread(NULL, 0, receive, &s, 0, NULL);
    char input[4096];
    char buff[4096];
    for(;;) {
        printf(">: ");
        scanf(" %[^\n]s", &input);
        sprintf(buff, "%s> %s", username, input);
        send(s, buff, sizeof(buff), 0);
    }

    return 0;
}

服务器是在 python 中编程的,虽然我认为这不是问题,因为当我在 send() 之后有 recv() 时它可以工作。

您正在将指向 SOCKET s 的指针传递给您的线程工作函数:

SOCKET s;
// ...
HANDLE handle = CreateThread(NULL, 0, receive, &s, 0, NULL);

该函数将传递的参数视为 SOCKET:

DWORD WINAPI receive(LPVOID s) {
    char output[4096];
    for(;;) {
        recv((SOCKET) s, output, sizeof(output), 0);
        printf("%s", output);
    }
    return 0;
}

所以基本上你不是从正确的文件描述符中读取而是从一些随机地址中读取。

取消引用辅助函数中的指针应该可以解决该问题:

DWORD WINAPI receive(LPVOID arg) {
    SOCKET *s = arg;
    char output[4096];
    for(;;) {
        recv(*s, output, sizeof(output), 0);
        printf("%s", output);
    }
    return 0;
}

不检查 recv 的 return 值并使用 printf 就好像 output 保证是一个以 null 结尾的字符串也是一个严重的问题。因此,我建议改为使用以下代码:

DWORD WINAPI receive(LPVOID arg) {
    SOCKET *s = arg;
    char output[4096];
    int len;
    for(;;) {
        len = recv(*s, output, sizeof(output), 0);
        if (len > 0)
            printf("%.*s", len, output);
    }
    return 0;
}

这样,output 就不必以 null 结尾。