我正在通过TCP构建一个多客户端<->服务器消息传递应用程序。
我使用epoll创建了一个非阻塞服务器来多路复用Linux文件描述符。
当fd接收数据时,我将buf读()/或/ recv()。
我知道我需要在传输开始时指定数据长度*,或者在传输结束时使用定界符**来分隔消息。
*使用数据长度:
char *buffer_ptr = buffer;
do {
switch (recvd_bytes = recv(new_socket, buffer_ptr, rem_bytes, 0)) {
case -1: return SOCKET_ERR;
case 0: return CLOSE_SOCKET;
default: break;
}
buffer_ptr += recvd_bytes;
rem_bytes -= recvd_bytes;
} while (rem_bytes != 0);
**使用分隔符:
void get_all_buf(int sock, std::string & inStr)
{
int n = 1, total = 0, found = 0;
char c;
char temp[1024*1024];
// Keep reading up to a '\n'
while (!found) {
n = recv(sock, &temp[total], sizeof(temp) - total - 1, 0);
if (n == -1) {
/* Error, check 'errno' for more details */
break;
}
total += n;
temp[total] = '\0';
found = (strchr(temp, '\n') != 0);
}
inStr = temp;
}
我的问题:是否可以遍历recv()直到满足其中一个条件?如果客户端发送的是假消息长度或没有定界符,或者有丢包怎么办?我不会永远在程序中循环使用recv()吗?
答案 0 :(得分:2)
是否可以遍历recv()直到满足其中一个条件?
可能不是,至少对于产品质量代码而言不是。正如您所建议的那样,直到收到完整的消息之前循环播放的问题是,它将线程留给客户端来摆布-如果客户端决定只发送部分消息然后等待很长时间(甚至永远) )而不发送最后一部分,那么您的线程将无限期地被阻塞(或循环),并且无法满足任何其他目的-通常不是您想要的目的。
如果客户端发送假消息长度怎么办
然后您就遇到了麻烦(尽管如果您选择了最大邮件大小,则可以检测到明显大于该大小的虚假邮件长度,并通过强制关闭连接来保护自己)
还是有丢包?
如果丢包的数量相当少,TCP层将自动重新传输数据,因此您的程序将不会注意到差异(除了消息正式“到达”时间晚于其他时间) 。如果确实存在严重的数据包丢失(例如,有人将以太网电缆从墙上拔了5分钟),则消息的其余部分可能会延迟几分钟或更长时间(直到连接恢复,或者TCP层放弃并关闭) TCP连接),将您的线程困在循环中。
那么,针对此难题的工业级,邪恶的客户端和可怕的网络防护解决方案是什么,以便即使特定客户端行为不当,您的服务器也可以保持对其他客户端的响应?
答案是这样的:不要依赖于一次接收全部消息。相反,您需要为每个客户端设置一个简单的状态机,以便您可以recv()
从该客户端的TCP套接字中{{1}}个字节(或尽可能少),只要它愿意在任何特定时间发送给您,并将这些字节保存到与该客户端关联的本地(每个客户端)缓冲区中,然后即使没有收到完整的消息,也要返回到正常事件循环。仔细跟踪每个客户端当前有多少有效接收字节数据,并在每个recv()调用返回后,检查相关的每客户端传入数据缓冲区是否包含一个是否完整消息-如果是,则解析消息,对其进行操作,然后将其从缓冲区中删除。泡沫,冲洗并重复。