c++ - C++ 服务器 recv() 命令并不总是从客户端接收 send()
问题描述
我有一个连接到客户端并执行以下通信的服务器:
void communicate(promise<long> && divisions, promise<long> && result, int sockt, long number, long prime) {
send(sockt, number);
send(sockt, prime);
long div = receive(sockt);
long res = receive(sockt);
divisions.set_value(div);
result.set_value(res);
}
void send(int sockt, long number) {
string numberString;
stringstream strstream;
strstream << number;
strstream >> numberString;
cout << "Sending: " << number << endl;
write(sockt, numberString.c_str(), sizeof numberString);
}
long receive(int sockt) {
// receive string
const unsigned int MAX_BUF_LENGTH = 1024;
std::vector<char> buffer(MAX_BUF_LENGTH);
std::string rcv;
recv(sockt, &buffer[0], buffer.size(), 0);
rcv.append(buffer.cbegin(), buffer.cend());
//convert string to long
string::size_type sz = 0;
long val = stoll(rcv, &sz, 0);
cout << "Received: " << val << endl;
return val;
}
客户端执行相同的通信,但相反:
void communicate(){
string number = receive(sockt);
string prime = receive(sockt);
// do stuff to the data
send(sockt, divisions);
send(sockt, val);
}
问题是服务器有时(在循环此通信 100 多次后)没有收到来自客户端的第二条发送消息并挂起。客户端从服务器接收消息永远不会遇到问题,它总是服务器挂起。服务器打印:
Sending: 344486269
Sending: 7
Received: 0
然后不会终止,所以我知道它在等待永远不会出现的第二个值。客户端打印:
Received: 344486269
Received: 7
Sending: 0
Sending: 344486269
所以我知道客户端成功执行了它的send()
命令。
什么可能导致服务器无法正确接收第二条消息?
解决方案
在void send(int sockt, long number)
中,您的呼叫write()
有误,原因如下:
sizeof numberString
是要发送的错误字节数。那是string
类的编译时大小,而不是它指向的字符数据的运行时字节大小。您不会以任何方式分隔发送的数据,以使接收者知道数据何时结束。发送可变长度数据时,您必须在发送数据之前发送数据长度,或者在数据之后发送唯一的终止符。
您没有考虑到
write()
发送的字节数少于请求的字节数的可能性。您必须循环调用它,直到所有所需的字节都已完全发送。
同样,在 中long receive(int sockt)
,您也有与 相关的类似问题recv()
。您只调用它一次,而不考虑正在发送的数据的大小。而且您忽略了它的返回值以了解实际读取了多少字节。
您需要更多类似以下的内容:
void send(int sockt, long number) {
ostringstream oss;
oss << number;
string numberString = oss.str();
// or simply:
// string numberString = to_string(number);
cout << "Sending: " << numberString << endl;
const char *ptr = numberString.c_str();
string::size_type size = numberString.size() + 1;
do {
int written = write(sockt, ptr, size);
if (written < 0) {
// error handling as needed...
return;
}
ptr += written;
size -= written;
}
while (size > 0);
}
long receive(int sockt) {
// receive string
char ch;
std::string rcv;
do {
if (recv(sockt, &ch, 1, 0) <= 0) {
// error handling as needed...
return -1;
}
if (ch == '\0') break;
rcv += ch;
}
while (true);
//convert string to long
string::size_type sz = 0;
long val = stoll(rcv, &sz, 0);
cout << "Received: " << val << endl;
return val;
}
但是,更好的解决方案是简单地long
以固定大小的二进制格式发送数据,而不是作为可变长度的字符串:
void send(int sockt, long number) {
uint32_t tmp = htonl(number);
cout << "Sending: " << number << endl;
char *ptr = reinterpret_cast<char*>(&tmp);
size_t size = sizeof(tmp);
do {
int written = write(sockt, ptr, size);
if (written < 0) {
// error handling as needed...
return;
}
ptr += written;
size -= written;
}
while (size > 0);
}
long receive(int sockt) {
// receive long
uint32_t tmp;
char *ptr = reinterpret_cast<char*>(&tmp);
size_t size = sizeof(tmp);
do {
int recvd = recv(sockt, ptr, size, 0);
if (recvd <= 0) {
// error handling as needed...
return -1;
}
ptr += recvd;
size -= recvd;
}
while (size > 0);
long val = ntohl(tmp);
cout << "Received: " << val << endl;
return val;
}
推荐阅读
- laravel - 我们如何在laravel中获得学校的最后一个ID?
- alexa - 如何从 alexa 语音资料或亚马逊帐户获取联系电话以开发 alexa 技能?
- java - Java 中的 ActionPerformed 方法
- php - 在 PHP7 中运行与 php 5.3 一起使用有什么问题?
- jquery - 使用 Id 在 Datalist 中设置选定值
- selenium-webdriver - 使用 TestNG (Java) 自定义范围报告
- javascript - html标签中名称的默认值是什么
- browser - 在 IIS 中禁用用户名/密码 Windows 身份验证
- javascript - onclick 仅在页面加载时起作用
- arrays - PowerShell:将制表符分隔文件导入为数组时遇到问题