如何读取套接字编程(c ++)中的所有数据?

How to read all the data in socket programming (c++)?

本文关键字:数据 编程 何读取 读取 套接字      更新时间:2023-10-16

我正在学习C++套接字编程。我已经通过 10 的缓冲区值初始化。我使用 select(( 函数来监视套接字。当客户端发送的数据大于我的缓冲区可以容纳的数据时,它将读取缓冲区大小的数据,并将再次遍历我的所有客户端并读取剩余数据。如何一次读取所有数据而不再次解析循环以读取剩余数据?

谢谢你的帮助。

for(i = 0;i < max_clients; i++){
sd = clients_list[i];
if(FD_ISSET(sd,&temp)){
char buf[10];
int n = recv(sd, buf, sizeof(buf),0);
if(n == -1){
perror("recv");
}else if(n == 0){
cout << "Client is GONE " << endl;
close(sd);
clients_list[i] = 0;
}
else{
buf[n] = '';
cout << "From the node: " << buf << endl;
}
}
}

只是为了完成我的评论 - 这个代码片段可能有助于理解

未测试的代码

for (i = 0;i < max_clients; i++) {
sd = clients_list[i];
if (FD_ISSET(sd,&temp)) {
char buf[10];
int n;
int flag;
do {
n = recv(sd, buf, sizeof(buf),0);
if (n > 0) {
buf[n] = '';
cout << "From the node: " << buf << endl;
ioctl(sd, FIONREAD, &flag);
} else {
flag = 0;
}
} while (flag > 0);
if (n < 0) {
perror("recv");
// in case of an error you may actively close the socket
// and end transmission by server
close(sd);
clients_list[i] = 0;
} else {
cout << "Client has currently no further transmission " << endl;
// don't close socket maybe later new transmission
// active close socket by server process only if
// e.g. a timeout has reached 
}
}
}

据我所知,您无法确定另一方发送的缓冲区大小。我认为你的方式是最好的处理方式,但你总是可以使用一些技巧。 1. 确定将作为常量发送的缓冲区大小,因此您每次都可能获得所需的数量。 2.我认为最好的方法:使用自制协议来确定发送的消息的长度。例如,您可以读取前 4 个字节以确定消息的大小,然后按给定大小从缓冲区读取数据。

您可以使用下一个转换:

char* c = new char[4];
recv(sd, c, sizeof(c), 0);
int len = (*(int*)c);
delete[] c;
char* but = new char[len];
recv(sd, but, len, 0);

首先,网络是不可靠和不可预测的,所以我们无法知道我们将收到多少字节,这是设计服务器的起点。

其次,我认为如果你想要一个更优雅的服务器,你可能想尝试 epoll 或 IOCP,它们仍然有循环,但 I/O 多路复用性能要好得多。

第三,如果你想接受所有的数据,你可以尝试构造一个缓冲区。在真实网络中,我们总是使用 kafka 或其他一些应用程序缓冲数据。

如何在不解析循环的情况下一次读取所有数据 再次读取剩余数据?

一般来说,您不能,因为所有数据可能还不可用。 如果需要,您可以告诉recv()阻止直到sizeof(buf)字节可用,方法是传入MSG_WAITALL而不是0到其最终("标志"(参数...但请注意,即使这样,在某些情况下,recv()也可能返回少于sizeof(buf)个字节(例如,如果捕获信号,或者在接收sizeof(buf)字节之前关闭连接,等等(。 因此,即使您使用MSG_WAITALL您仍然希望实现短读取处理逻辑以获得 100% 正确的行为。

另请注意,发送方没有义务及时发送您期望的所有字节,网络也没有义务及时发送所有字节。 因此,发送方完全有可能向您发送sizeof(buf)-1字节,然后在发送最后一个字节之前等待 15 分钟,如果您在等待最后一个字节的recv()调用中被阻止,那么您的服务器将在这么长时间内对所有其他客户端无响应。

因此,在实现这样的多路复用/单线程服务器时,通常最好将套接字设置为非阻塞模式,并为每个客户端的套接字保留单独的接收数据缓冲区。 这样,您可以遍历套接字列表,从每个套接字recv()尽可能多的数据(不阻塞(,并将该数据附加到该套接字的关联缓冲区,然后检查缓冲区以查看其中是否有足够的数据来处理下一个块,如果这样做,则处理块,然后从缓冲区中删除数据, 并继续。 这样,客户端 B 就不必等待客户端 A 完成向服务器发送消息。

我已经将ioctl((函数与FIONREAD一起使用,以检查是否有更多数据可以读取它,并且它一直在工作。

这是代码:

for(i = 0;i < max_clients; i++){
sd = clients_list[i];
if(FD_ISSET(sd,&temp)){
receive_more:
char buf[10];
int arg = 0;
int n = recv(sd, buf, sizeof(buf),0);
if(n == -1){
perror("recv");
}else if(n == 0){
cout << "Client is GONE " << endl;
close(sd);
clients_list[i] = 0;
}
else{
buf[n] = '';
cout << "From the node: " << buf << endl;
ioctl(sd,FIONREAD,&arg);
if(arg > 0){
goto receive_more;
}
}
}
}