Reputation: 13
I am learning socket programming in C++. I have initialised by buffer value at 10. I have used select() function to monitor the socket. When the client sends data which is greater than my buffer can accommodate, it will read buffer size worth of data and will again go through the all my clients and read the remaining data. How can I read all the data at once without parsing through the loop again to read the remaining data ?
Thank you for your help.
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] = '\0';
cout << "From the node: " << buf << endl;
}
}
}
Upvotes: 0
Views: 4219
Reputation: 1367
Just to complete my comment - this code snippet may help to understand
not tested code
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] = '\0';
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
}
}
}
Upvotes: 1
Reputation: 13
I have used ioctl() function with FIONREAD to check whether there is more data to read it or not and it has been working.
Here is the code:
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] = '\0';
cout << "From the node: " << buf << endl;
ioctl(sd,FIONREAD,&arg);
if(arg > 0){
goto receive_more;
}
}
}
}
Upvotes: 0
Reputation: 73041
How can I read all the data at once without parsing through the loop again to read the remaining data ?
Generally speaking, you can't, since all of the data might not be available yet. If you want, you can tell recv()
to block until sizeof(buf)
bytes are available, by passing in MSG_WAITALL
instead of 0
to its final ("flags") argument... but note that even then it is possible for recv()
to return fewer than sizeof(buf)
bytes in some cases (e.g. if a signal is caught, or the connection is closed before sizeof(buf)
bytes are received, and so on). So even if you use MSG_WAITALL
you will still want to implement short-read-handling logic in order to get 100% correct behavior.
Also note that the sender is under no obligation to send all of the bytes you are expecting in a timely manner, nor is the network under any obligation to deliver all of the bytes in a timely manner. So it's perfectly possible that a sender might send sizeof(buf)-1
bytes to you, then wait 15 minutes before sending the last byte, and if you are blocked in a recv()
call waiting for that last byte, then your server will be unresponsive to all of its other clients during that long period.
Therefore when implementing a multiplexed/single-threaded server like this, it's usually best to set the sockets to non-blocking mode, and keep a separate received-data buffer for each client's socket. That way you can loop through your sockets list, recv()
as much data as you can from each socket (without blocking), and append that data to that socket's associated buffer, then check the buffer to see if you have enough data in it yet to handle the next chunk, and if you do, handle the chunk and then remove the data from the buffer, and continue. That way client B never has to wait for client A to finish sending a message to the server.
Upvotes: 0
Reputation: 1494
Firstly, the network is unreliable and unpredictable, so we can not know how many bytes we will receive, that is the start point of designing a server.
Secondly, I think if you want a more elegent server, you may want to try epoll or IOCP, they still have loops, but the I/O multiplexing performance are much better.
Thirdly, if you want to accept all the data, you may try to construct a buffer. In real network, we always buffer the data using kafka or some other apps.
Upvotes: 0
Reputation: 176
By my knowledge you can't determine the buffer size that was sent by the other side. I think your way is the best way to deal with it, but you always can use some tricks. 1. Determine the buffer size that will be sent as a constant and thus you may get the wanted amount every time. 2. A way which I think is the best: Use a self made protocol to determine the length of the message that was sent. You may for example read the 4 first bytes to determine the size of your message and then read the data off the buffer by the given size.
You can use the next transformation:
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);
Upvotes: 0