我正在尝试构建一个非常简单的套接字示例,其中客户端连接到服务器,从 stdin 读取数据并通过 TCP 环回套接字将其发送到服务器。
我的问题:服务器上对
recv
的第一次调用按预期工作,但循环中的第二次调用总是返回0,表明客户端已关闭连接。客户端仍然提示我输入,但服务器没有收到任何内容。但是,我无法理解这是如何可能的,因为客户端仅重复输入并循环发送。
#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;
}
#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;
}
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
服务器读取循环的关键部分是:
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