我有一个客户端和一个在Windows上使用套接字的服务器.我在启动客户端之前启动服务器,等待它挂起,然后启动客户端.这样做,对recv()的调用将随机(一半时间)返回-1,当我打印errno时,它是0(无错误)...另一半的时间它会收到消息并正常打印,然后关闭...这个代码有什么问题吗?

client.c

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <errno.h>

#define BUFFER_LEN 200

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);
    
    SOCKET sock;
    SOCKADDR_IN address;
    // address.sin_addr.s_addr = inet_addr("127.0.0.1");
    InetPton(AF_INET, "127.0.0.1", &address.sin_addr.s_addr);
    address.sin_family = AF_INET;
    address.sin_port = htons(8888);
    
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("Erreur lors de la création du socket. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if ((connect(sock, (SOCKADDR *)&address, sizeof(address))) == -1) {
        sprintf("Erreur lors de la connexion au socket. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    char *msg = "Coucou\n";
    printf("Envoi de coucou\n");
    int bytesSent;

    if ((bytesSent = send(sock, msg, strlen(msg), 0)) == -1) {
        sprintf("Erreur lors l'envoi du message. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("Bytes envoyés : %d\n", bytesSent);

    WSACleanup();
    
    return 0;
}

server.c

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define BACKLOG 3 // taille de la file d'attente
#define BUFFER_LEN 200 // taille du buffer de lecture

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2,0), &WSAData);
    
    SOCKET sockfd;
    SOCKADDR_IN address;
    // address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_addr.s_addr = htonl(INADDR_ANY);  
    address.sin_family = AF_INET;
    address.sin_port = htons(8888); // host to network short (conversion de host byte order en network byte order)
    
    printf("Démarrage du serveur\n");

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("Erreur lors de la création du socket. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if ((bind(sockfd, (SOCKADDR *)&address, sizeof(address))) == -1) {
        printf("Erreur lors de l'ouverture du socket. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    if ((listen(sockfd, BACKLOG)) == -1) {
        printf("Erreur lors de l'écoute du socket. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    SOCKET clientSocket;
    SOCKADDR_IN clientAddress;
    int addrLen = sizeof(clientAddress);
    if ((clientSocket = accept(sockfd, (struct sockaddr *) &clientAddress, &addrLen)) == -1) {
        printf("Erreur lors de l'acceptation de la connexion. %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

    // Convertion de l'IP en texte
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(clientAddress.sin_addr), ip, INET_ADDRSTRLEN);
    printf("Connexion de %s:%i\n", ip, clientAddress.sin_port);

    char buffer[BUFFER_LEN];
    int len = recv(clientSocket, buffer, BUFFER_LEN, 0);

    if (len == -1 && errno != EAGAIN) {
        printf("Erreur lors de la réception du message. %s (%d)\n", strerror(errno), errno);
        exit(EXIT_FAILURE);
    } else if (len == 0) {
        printf("Le client s'est déconnecté (extrémité de la socket fermée)\n");
        exit(EXIT_FAILURE);
    } else if (len > 0) {
        printf("Message reçu : %s\n", buffer);
    }

    closesocket(clientSocket);
    closesocket(sockfd);

    WSACleanup();

    return 0;
}

我还应该提一下,我用gcc main.c -lws2_32 -g -Wall编译

推荐答案

首先要注意的是,当客户端中对send()的调用返回时,not表示所有数据都已发送到您的服务器.这只是意味着数据已经排队等待发送.

然后,您的客户立即拨打WSACleanup()并退出.看起来,在您的套接字还没有来得及发送任何东西之前,这会立即 destruct 您的套接字.

你应该在发送数据和拨打WSACleanup()之间加closesocket(sock).这将允许以有序的方式关闭套接字,包括刷新已排队的任何数据.

正如@Hans所指出的,读取errno不是获取套接字错误的正确方法,您应该使用WSAGetLastError().

C++相关问答推荐

我可以动态分配具有空类型函数的矩阵吗?

为什么listen()(在调用accept()之前)足以让应用程序完成3次握手?

Mise()在虚拟内存中做什么?

使用额外的公共参数自定义printf

Can函数指针指向C++中具有不同参数连续性的函数

LibpCap禁用监视器模式(C、MacOS)

ATmega328P EEPROM未写入

如何创建一个C程序来存储5种动物的名字,并在用户 Select 其中任何一种动物时打印内存地址?

正确的TCP/IP数据包 struct

当输入负数时,排序算法存在问题

使用nmake for程序比Hello World稍微复杂一些

正数之和是负数

OSDev--双缓冲重启系统

用C++高效解析HTTP请求的方法

C++中PUTS函数的返回值

将某些内容添加到链接列表时,列表中的其他项将使用最后添加的项的名称

我正在使用 klib 库 我可以使用 (khash) KHASH_SET_INIT_INT64() 负值作为键.因为我在头文件中看到它使用 unsigned long int

C 中从 Unix 纪元时间转换的损坏

在带中断的循环缓冲区中使用 易失性

在 printf() 格式说明符中使用字段宽度变量