send() 或 recv() 不同步

send() or recv() not syncing up

我一直在做 Winsock tutorials 并完全按照它进行。我似乎无法使 send()recv() 正常运行。我设置了基本的服务器和客户端程序,并且正在建立连接,但服务器没有向客户端发送消息。使用 Telnet 客户端也收不到服务器的响应。我不太确定发生了什么,而且我看到的所有问题都不是基本问题,或者有一些我无法真正理解他们在做什么的问题。

Server.cpp

#include<WinSock2.h>
#include <io.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c;
    char *message;

    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    else
        printf("Initialised.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 2;
    }
    else
        printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : &d", WSAGetLastError());
        return 3;
    }
    else
        puts("Bind done");

    //listen
    listen(s, 3);

    //accept an incoming connection
    puts("Waiting for incoming connections...");

    c = sizeof(sockaddr_in);

    while (new_socket = accept(s, (sockaddr *)&client, &c) != INVALID_SOCKET)
    {
        printf("Connect successful...\n");

        //reply to the client
        message = "Hello Client, I have recieved your connection, but I have to go now, bye!\n";
        send(new_socket, message, strlen(message), 0);

        puts("Message sent.\n");
    }

    if (new_socket == INVALID_SOCKET)
    {
        printf("accept() failed with error code : %d", WSAGetLastError());
        return 4;
    }

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

Client.cpp

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int main(int argc, char *argv[])
{
    //intialize variables
    WSADATA wsa;
    char ip[100] = "192.168.1.117";
    SOCKET s;
    sockaddr_in server;
    char *message, server_reply[75];
    int recv_size;

    //initialize Winsock
    printf("\nInitialising Winsock...\n");
    if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    printf("Initialised.\n");

    //create the socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 3;
    }
    printf("Socket created.\n");

    server.sin_addr.S_un.S_addr = inet_addr(ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(8888);

    //connect to the server
    if (connect(s, (sockaddr *)&server, sizeof(server)) < 0)
    {
        puts("connect error");
        return 4;
    }
    else
    {
        printf("Connect successful");

        recv_size = recv(s, server_reply, 75, 0);
    }

    if (recv_size <= 0)
    {
        puts("recv() failed\n");
    }
    else
    {
        //add a NULL terminating character to make it a proper string before printing
        server_reply[recv_size] = '[=11=]';
        puts(server_reply);
    }

    getchar();

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

客户端也无法打印"recv() failed"行;就好像它卡在了 recv() 电话上。

来自关于 closesocket() 的 MSDN 文档:

Note To assure that all data is sent and received on a connection, an application should call shutdown before calling closesocket (see Graceful shutdown, linger options, and socket closure for more information). Also note, an FD_CLOSE network event is not posted after closesocket is called.

基本上,当您关闭套接字时,您发送的数据仍然处于待处理状态。

在服务器端:

  • 您没有检查 listen() 的 return 值是否有误。

  • 您没有在每次调用 accept() 时重置 c,并且您没有在每个被接受的客户端上调用 closesocket()

  • 您没有检查 send() 的 return 值是否有误。

试试这个:

#include <WinSock2.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c, res, messagelen;
    const char *message;

    printf("\nInitializing Winsock...");
    res = WSAStartup(MAKEWORD(2,2), &wsa);
    if (res != 0)
    {
        printf("Failed. Error: %d\n", res);
        return 1;
    }
    printf("Initialized.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket. Error: %d\n", WSAGetLastError());
        return 2;
    }
    printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed. Error: &d", WSAGetLastError());
        return 3;
    }
    printf("Bind done.\n");

    //listen
    if (listen(s, 3) == SOCKET_ERROR)
    {
        printf("Listen failed. Error: &d", WSAGetLastError());
        return 4;
    }
    printf("Listening.\n");

    //accept incoming connections
    printf("Waiting for incoming connections...\n");

    do
    {
        c = sizeof(sockaddr_in);

        new_socket = accept(s, (sockaddr *)&client, &c);
        if (new_socket == INVALID_SOCKET)
        {
            printf("Failed to accept a client. Error: %d\n", WSAGetLastError());
            return 5;
        }

        printf("Client connected...\n");

        //reply to the client
        message = "Hello Client, I have received your connection, but I have to go now, bye!\n";
        messagelen = strlen(message);

        do
        {
            res = send(new_socket, message, messagelen, 0);
            if (res == SOCKET_ERROR)
            {
                printf("Failed to send message. Error: %d\n", WSAGetLastError());
                break;
            }

            message += res;
            messagelen -= res;
        }
        while (messagelen > 0);

        if (messagelen == 0)
            printf("Message sent.\n");

        //close the client socket
        closesocket(new_socket);
    }
    while (true);

    //close the server socket
    closesocket(s);
    WSACleanup();

    return 0;
}

在客户端,我看到的唯一真正的问题是你的 recv() 调用有一个潜在的缓冲区溢出等待发生,因为你要求它读取 75 个字节,这就是你的确切大小缓冲。碰巧您的服务器最多只发送 74 个字节,但如果发送更多字节,则在向其附加 '[=19=]' 终止符时可能会溢出缓冲区。

所以,要么:

  • sizeof(server_reply)-1作为缓冲区大小调用recv(),为添加的终止符留出空间:

    recv_size = recv(s, server_reply, sizeof(server_reply)-1, 0);
    
  • 使用 printf() 而不是 puts() 因此在将缓冲区打印到控制台时根本不需要以 null 终止缓冲区。您可以将 recv_size 作为参数传递以限制打印的文本量:

    //server_reply[recv_size] = '[=12=]';
    //puts(server_reply);
    printf("%.*s", recv_size, server_reply);