发送 recv 调用时带有 C++ 字符串的垃圾文本
garbage text with c++ strings on send recv calls
// In server.cpp after connection has established
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore(); // =====(1)=====
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0); // =====(2)=====
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
我的目标是在简单的 tcp 客户端服务器程序中使用 std::string
而不是普通的旧char[fixed]
。所以在server.cpp
我有 2 个疑问。到目前为止,我最初的猜测正在按预期工作。我已经在上面的代码中标记了它们。
-
cin.ignore()
vscin.clear()
+cin.sync()
-
std::string.data()
与std::string.c_str()
我应该使用哪个?我什至不确定这些中的任何一个黑白的区别,我不知道它们是否导致了我的问题。
// In client.cpp
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0); // =====(1)=====
message[len] = 0; // =====(2)=====
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl; // =====(3)=====
在客户端.cpp中,当我尝试发送更大的字符串时,一切都出错了。但我可能知道原因,但是解决方案实施起来有些棘手。
- 我是否在做正确的事情来传递
&std::string.front()
来写入传入数据? - 这是错误的,字符串类应该管理这个,对吧?但是,由于我直接写信给
&front()
,我想length
不会更新,或者我不确定会发生什么,但是在使用std::cout << message;
输出时数据肯定会丢失。 - 我这样做只是因为我直接写入
&front
,如果返回的数据以某种方式小于总长度,它仍然会产生垃圾,可能是因为它无法在正确的位置找到终止字符?
服务器.cpp
// compile as 'g++ server.cpp -o server.app -std=c++14'
// run as : './server.app 8080'
#include <iostream>
#include <string>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 2) {
std::cerr << "Run program as 'program port'" << std::endl;
return -1;
}
auto &portNum = argv[1];
const unsigned int backLog = 5;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(NULL, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
freeaddrinfo(res);
return -4;
}
int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen);
if(bindR == -1) {
std::cerr << "Error while binding socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -5;
}
int listenR = listen(sockFD, backLog);
if(listenR == -1) {
std::cerr << "Error while Listening on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -6;
}
struct sockaddr_storage client_addr;
socklen_t client_addr_size = sizeof(client_addr);
int newFD =
accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size);
if(newFD == -1) {
std::cerr << "Error while Accepting on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -7;
}
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore();
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0);
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
close(newFD);
close(sockFD);
freeaddrinfo(res);
return 0;
}
客户端.cpp
// compile as 'g++ client.cpp -o client.app -std=c++14'
// run as : './client.app 0 8080'
#include <iostream>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 3) {
std::cerr << "Run program as 'program ipaddress port'" << std::endl;
return -1;
}
auto &ipAddress = argv[1];
auto &portNum = argv[2];
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
return -4;
}
int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);
if(connectR == -1) {
close(sockFD);
std::cerr << "Error while connecting socket" << std::endl;
return -5;
}
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);
message[len] = 0;
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl;
return 0;
}
-
你对此的问题太模糊了,无法提供有用的答案。
-
自 C++11 年以来,
data()
和c_str()
实际上是一回事。使用哪一个并不重要。编辑:在C++17中,data()
将有一个非const
重载,返回一个非const
char*
,因此您无需执行&message.front()
即可访问可修改形式的底层缓冲区。c_str()
将保持const
. -
&message.front()
是对的...错了。这是获得对std::string
内容的非常量char*
的方法。但是message
是未初始化的,并且在代码的那个点有size()
0
,所以我甚至不确定那行代码是明确定义的行为。与其做reserve(5)
,不如这样构造你的string
:auto message = std::string(5, ' ');
然后当你把它传递到recv
时,那里实际上会有有效的东西供它覆盖,之后你可以从message
读取它。 -
是的,这是错误的。您应该将字符串设置为所需的实际大小。我怀疑如果你这样做,你可以通过
len
而不是len - 1
。关于这个主题,您确定您收到的所有内容只有 4 个字节长吗?还是您有意一次只读取 4 个字节? -
a( 您不需要将
c_str()
传递给std::cout
。<<
也超载接受std::string
。b(recv
返回您收到的字节数。如果该值小于您将message
初始化为的大小,则字符串中的其余字符将是垃圾(如果您遵循我的建议,则为' '
char
s:#3(。收到消息后我会做message.resize(bytes_recv);
。
你的问题已经用大写字母解决了,但我的 2 美分。拥有自己的发送/接收功能并隐藏复杂性怎么样?
例如,沿着以下行:
ssize_t recv(int sockfd, std::string &buf, size_t len, int flags) {
buf.resize(len); // current status unknown -> make it fit
ssize_t n = ::recv(sockfd, (void *)buf.data(), len, flags);
buf.resize(n >= 0 ? n : 0); // take error into account
return n;
}
- 如何在C++中用std::cout正确显示带十六进制的字符串文本
- 在编译时检查字符串文本的长度
- 读取字符串文本输入以创建 2D 矢量
- 管理字符串文本的最佳做法
- 定义宏以将前缀 0x 添加到十六进制字符串文本
- 无法在模板参数中定义字符串文本
- 不推荐使用 PTCHAR 的字符串文本
- C++ 字符串文本和常量
- C++20字符串文本模板参数工作示例
- 返回从字符串文本创建的静态string_view是否安全?
- 在处理任何字符大小的模板中使用字符串文本
- 是否保证相同内容字符串文本的存储相同?
- 将以 null 结尾的字节字符串转换为原始字符串文本
- 是否可以创建一个用户定义的文本,将字符串文本转换为 own 类型的数组?
- 使用C++中的模板检测不同的字符串文本
- 无法完全专用化字符串文本的模板
- C++ - 确定 const char* 是指向字符串文本对象还是动态对象
- 是否可以在原始字符串文本中插入转义序列?
- C++带有捕获组的正则表达式字符串文本
- 为什么多维数组中的空字符串文本衰减为空指针?