为什么我的客户端代码在第一次发送调用后关闭连接?

问题描述 投票:0回答:1

我正在尝试构建一个非常简单的套接字示例,其中客户端连接到服务器,从 stdin 读取数据并通过 TCP 环回套接字将其发送到服务器。

我的问题:服务器上对

recv
的第一次调用按预期工作,但循环中的第二次调用总是返回0,表明客户端已关闭连接。客户端仍然提示我输入,但服务器没有收到任何内容。但是,我无法理解这是如何可能的,因为客户端仅重复输入并循环发送。

客户端.cpp

#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>

#include <iostream>

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <port number>" << std::endl;
        return 1;
    }

    addrinfo hints{.ai_protocol = IPPROTO_TCP,
                   .ai_socktype = SOCK_STREAM,
                   .ai_family = PF_INET};

    addrinfo* result;

    int getaddrresult = getaddrinfo("localhost", argv[1], &hints, &result);
    if (getaddrresult != 0) {
        std::cerr << "getaddrinfo failed: " << gai_strerror(getaddrresult) << std::endl;
        return 1;
    }

    if (result == nullptr) {
        std::cerr << "getaddrinfo returned nullptr" << std::endl;
        return 1;
    }

    int sockFd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

    if (sockFd == -1) {
        std::cerr << "socket failed" << std::endl;
        return 1;
    }

    int connectResult = connect(sockFd, result->ai_addr, result->ai_addrlen);

    if (connectResult == -1) {
        std::cerr << "connect failed" << std::endl;
        return 1;
    }

    std::cout << "Connected to server" << std::endl;

    while (true) {
        std::string message;
        std::cout << "Please enter message: ";
        std::getline(std::cin, message);

        if (message == "exit") {
            break;
        }

        int sendResult = send(sockFd, message.c_str(), message.size(), 0);

        if (sendResult == -1) {
            perror("send failed");
            return 1;
        }

        std::cout << "Sent " << sendResult << " bytes" << std::endl;
    }

    std::cout << "Closing connection" << std::endl;
    close(sockFd);
    freeaddrinfo(result);


    return 0;
}

服务器.cpp

#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <iostream>
#include <stdio.h>
#include <vector>

using namespace std;

pair<string, string> getHostAndPort(const sockaddr *addr) {
    char host[NI_MAXHOST];
    char port[NI_MAXSERV];
    int getnameinforesult = getnameinfo(addr, sizeof(addr->sa_len), host,
                                        NI_MAXHOST, port, NI_MAXSERV, 0);
    if (getnameinforesult != 0) {
        return {"<unknown>", "<unknown>"};
    }
    return {host, port};
}

int main() {
    addrinfo hints{.ai_protocol = IPPROTO_TCP,
                   .ai_socktype = SOCK_STREAM,
                   .ai_family = PF_INET};
    addrinfo *result;
    const char *port = "50069";
    int getaddrresult = getaddrinfo("localhost", port, &hints, &result);
    if (getaddrresult != 0) {
        cout << "getaddrinfo failed: " << gai_strerror(getaddrresult) << endl;
        return -1;
    }

    if (result == nullptr) {
        cout << "getaddrinfo returned nullptr" << endl;
        return -1;
    }

    const auto [host, portString] = getHostAndPort(result->ai_addr);
    int sockFd =
        socket(result->ai_family, result->ai_socktype, result->ai_protocol);

    if (sockFd == -1) {
        perror("socket failed");
        return -1;
    }

    if (bind(sockFd, result->ai_addr, result->ai_addrlen) == -1) {
        perror("bind failed");
        return -1;
    }
    cout << "bound to port " << portString << endl;

    if (listen(sockFd, 10) == -1) {
        perror("listen failed");
        return -1;
    }

    cout << "listening for incoming connections on " << host << ":"
         << portString << endl;

    sockaddr_storage clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);
    while (true) {
        int clientFd = accept(sockFd, (sockaddr *)&clientAddr, &clientAddrLen);
        if (clientFd == -1) {
            perror("accept failed");
            return -1;
        }
        const auto [clientHost, clientPort] =
            getHostAndPort((sockaddr *)&clientAddr);
        cout << "accepted connection from " << clientHost << ":" << clientPort
             << endl;

        std::vector<char> buffer(1024);
        int bytesReceived;
        while (true) {
            bytesReceived = recv(clientFd, buffer.data(), buffer.size(), 0);
            if (bytesReceived == -1) {
                perror("recv failed");
                return -1;
            } else if (bytesReceived > 0) {
                std::string receivedData(buffer.data(), bytesReceived);
                cout << "received " << bytesReceived << " bytes: <"
                     << receivedData << ">" << endl;
            } else if (bytesReceived == 0) {
                cout << "client closed connection" << endl;
                break;
            }
            buffer.clear();
        }

        close(clientFd);
        cout << "closed connection to " << clientHost << ":" << clientPort
             << endl;
    }
    close(sockFd);

    freeaddrinfo(result);
    return 0;
}


CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 17)

project(server_client)

add_compile_options(-Wall -Wextra -Wpedantic -Werror)

add_executable(server src/server.cpp)

add_executable(client src/client.cpp)

预期行为: 下一次迭代中对

recv
的第二次调用会阻塞,直到再次接收到数据。

实际产量

服务器:

./server
bound to port 50069
listening for incoming connections on localhost:50069
accepted connection from localhost:65036
received 15 bytes: <fshdjkfhkdjsfsd>
client closed connection
closed connection to localhost:65036
^C

客户

./client 50069
Connected to server
Please enter message: fshdjkfhkdjsfsd
Sent 15 bytes
Please enter message: fhdjskfhksd
Sent 11 bytes
Please enter message: ^C
c++ sockets unix tcp
1个回答
0
投票

服务器读取循环的关键部分是:

        std::vector<char> buffer(1024);
               :
        while (true) {
            bytesReceived = recv(clientFd, buffer.data(), buffer.size(), 0);
               :
            buffer.clear();
        }
~~~

So you have a buffer that you initialize as 1024 bytes, and then at the end of the loop, you resize it to 0 bytes (that is what `.clear()` does).  That means that the second call to `recv` has a size of `0`, and so returns `0`, which your the treat as an EOF.

Remove the buffer.clear() and things should work
© www.soinside.com 2019 - 2024. All rights reserved.