In an application I'm currently working on, I need to stop downloading some file if I realize it's not what I'm looking for. The protocol doesn't provide any way to know it before I start receiving the file (like headers or so).
As an example, in some cases I might be looking for a file of exactly X bytes in size, but after I have downloaded X bytes and I keep getting more bytes, this is not the file I'm looking for as its size is greater than X. In this case, I want to stop downloading to free network bandwidth resources. The protocol doesn't provide any way to notify the server about this.
I read somewhere that close(fd) or shutdown(fd, SHUT_RD) won't actually stop downloading as the server will continue to send() the file and this will continue to consume network bandwidth. I am also not sure about if I just stop calling recv() and packets still arrive, will they fill the buffer and then start to be discarded? If it matters, the protocol used is based on TCP (but I would like some solution that can also be used for UDP based protocols).
I became even more doubtful that stopping calls to recv() would solve it, after I searched for programmatic bandwidth control (sleep(), token bucket..) as an alternative solution (reduce download speed to about zero after I realize it's not the file I'm looking for). How can I control network bandwidth usage by reducing recv() calls if the server will still be send()ing? I didn't catch it.
Main idea is to entirely stop the download.
What would you suggest?
I read somewhere that close(fd) or shutdown(fd, SHUT_RD) won't actually stop download as server will continue to send() file and this will continue to consume network bandwidth.
If you shutdown(fd, SHUT_RD) your own recv() will unblock with a return code of zero, which will cause the code to close the socket, which will cause the localhost to issue an RST if any more data comes from the peer, which will cause an ECONNRESET at the sender (after a few more send() calls, not necessarily immediately).
Where did you read this nonsense?
I became even more doubtful about if stop calling recv() would solve it
It won't solve it, but it will eventually stop the sender from sending, because of TCP flow control. It isn't a solution to this problem.
Related
Im working with TCP Sockets in C but yet dont really understand "how far" the delivery of data is ensured.
My main problem is that in my case the server sometimes sends a message to the client and expects an answer shortly after. If the client doesnt answer in time, the server closes the connection.
When reading through the manpages of the recv() function in C, I found the MSG_PEEK Flag which lets me look/peek into the Stream without actually reading the data.
But does the server even care if I read from the stream at all?
Lets say the server "pushes" a series of messages into the stream and a Client should receive them.
As long as the Client doesnt call recv() those messages will stay in the Stream right?
I know about ACK messages being send when receiving data, but is ACK sent when i call the recv() function or is the ACK already sent when the messsage successfully reached its destination and could (emphasising could) be received by the client if it choses to call recv()?
My hope is to trick the server into thinking the message wasnt completely send yet, because the client has not called recv() yet. Therefore the Client could already evaluate the message by using the MSG_PEEK flag and ensure it always answers in time.
Of course I know the timout thing with my server depends on the implementation. My question basically is, if PEEKING lets the server think the message hasnt reached it destination yet or if the server wont even care and when ACK is sent when using recv().
I read the manpages on recv() and wiki on TCP but couldnt really figure out how recv() takes part in the process. I found some similar questions on SO but no answer to my question.
TL;DR
Does the recv() function trigger sending the ACK?
No, not on any regular OS. Possibly on an embedded platform with an inefficient network stack. But it's almost certainly the wrong problem anyway.
Your question about finessing the details of ACK delivery is a whole can of worms. It's an implemention detail, which means it is highly platform-specific. For example, you may be able to modify the delayed ACK timer on some TCP stacks, but that might be a global kernel parameter if it even exists.
However, it's all irrelevant to your actual question. There's almost no chance the server is looking at when the packet was received, because it would need it's own TCP stack to even guess that, and it still wouldn't be reliable (TCP retrans can keep backing off and retrying for minutes). The server is looking at when it sent the data, and you can't affect that.
The closest you could get is if the server uses blocking writes and is single-threaded and you fill the receive window with un-acked data. But that will probably delay the server noticing you're late rather than actually deceiving it.
Just make your processing fast enough to avoid a timeout instead of trying to lie with TCP.
I have a server that is running a select() loop that sometimes continues blocking when the client closes the connection from its side. The select() loop handles all other read/write operations correctly and sets the correct file descriptor in the fd_set, leading me to believe that it is not an issue with the file descriptor setup on the server-side.
The way I planned on handling the client closing the connection was to have the select() break due to activity on the socket (closing it from the client-side), see that the fd was set for that socket, and then try to read from it - and if the read returned 0, then close the connection. However, because the select() doesn't always return when the client side closes the connection, there is no attempt to check the fd_set and subsequently try to read from the socket.
As a workaround, I implemented a "stop code" that the client writes to the server just before closing the connection, and this write causes the select() to break and the server reads the "stop code" and knows to close the socket. The only problem with this solution is the "stop code" is an arbitrary string of bytes that could potentially appear in regular traffic, as the normal data being written can contain random strings that could potentially contain the "stop code". Is there a better way to handle the client closing the connection from its end? Or is the method I described the general "best practice"?
I think my issue has something to do with OpenSSL, as the connection in question is an OpenSSL tunnel, and it is the only file descriptor in the set giving me issues.
The way I planned on handling the client closing the connection was to have the select() break due to activity on the socket (closing it from the client-side), see that the fd was set for that socket, and then try to read from it - and if the read returned 0, then close the connection. However, because the select() doesn't always return when the client side closes the connection, there is no attempt to check the fd_set and subsequently try to read from the socket.
Regardless of whether you are using SSL or not, select() can tell you when the socket is readable (has data available to read), and a graceful closure is a readable condition (a subsequent read operation reports 0 bytes read). It is only abnormal disconnects that select() can't report (unless you use the exceptfds parameter, but even that is not always guaranteed). The best way to handle abnormal disconnects is to simply use timeouts in your own code. If you don't receive data from the client for awhile, just close the connection. The client will have to send data periodically, such as a small heartbeat command, if it wants to stay connected.
Also, when using OpenSSL, if you are using the older ssl_... API functions (ssl_new(), ssl_set_fd(), ssl_read(), ssl_write(), etc), make sure you are NOT just blindly calling select() whenever you want, that you call it ONLY when OpenSSL tells you to (when an SSL read/write operation reports an SSL_ERROR_WANT_(READ|WRITE) error). This is an area where alot of OpenSSL newbies tend to make the same mistake. They try to use OpenSSL on top of pre-existing socket logic that waits for a readable notification before then reading data. This is the wrong way to use the ssl_... API. You are expected to ask OpenSSL to perform a read/write operation unconditionally, and then if it needs to wait for new data to arrive, or pending data to send out, it will tell you and you can then call select() accordingly before retrying the SSL read/write operation again.
On the other hand, if you are using the newer bio_... API functions (bio_new(), bio_read(), bio_write(), etc), you can take control of the underlying socket I/O and not let OpenSSL manage it for you, thus you can do whatever you want with select() (or any other socket API you want).
As a workaround, I implemented a "stop code" that the client writes to the server just before closing the connection, and this write causes the select() to break and the server reads the "stop code" and knows to close the socket.
That is a very common approach in many Internet protocols, regardless of whether SSL is used or not. It is a very distinct and explicit way for the client to say "I'm done" and both parties can then close their respective sockets.
The only problem with this solution is the "stop code" is an arbitrary string of bytes that could potentially appear in regular traffic, as the normal data being written can contain random strings that could potentially contain the "stop code".
Then either your communication protocol is not designed properly, or your code is not processing the protocol correctly. In a properly-designed and correctly-processed protocol, there will not be any such ambiguity. There needs to be a clear distinction between the various commands that your protocol defines. Your "stop code" would be one such command amongst other commands. Random data in one command should not be mistakenly treated as a different command. If you are experiencing that problem, you need to fix it.
I'm writing a C/S program and both the client and server may send data to peer (without explicit ack) at arbitrary time. I'm wondering if it could possibly deadlock if the client and server coincidentally write to the peer at the same time.
So does returning from write() mean that the peer application has already read() the data? Or it only means the peer's kernel has got the data and would deliver to the app on next read()?
(EJP's answer fixed my totally wrong understanding about write()/send()/.... To add some authoritative info I found this in the POSIX standard about send:
Successful completion of a call to send() does not guarantee delivery of the message. A return value of -1 indicates only locally-detected errors.
Linux's man page about send() is not very clear:
No indication of failure to deliver is implicit in a send(). Locally detected errors are indicated by a return value of -1.
Or it's because I cannot fully understand the first sentence as a non native English speaker. )
I'm wondering if it could possibly deadlock if the client and server coincidentally write to the peer at the same time.
It can't, unless one or both of the peers is very slow reading and has closed its receive window. TCP is full-duplex.
So does returning from write() mean that the peer application has already read() the data?
No.
Or it only means the peer's kernel has got the data and would deliver to the app on next read()?
No.
It means the data has reached your kernel, and is queued for transmission.
the returning from write() means the TCP ACK has already been received.
No it doesn't.
u mean returning from write() only means the data has reached the sender's kernel?
That is not only what I meant, it is what I said.
I'd think the sender's already received the TCP ACK so reached the peer's kernel.
No.
No. If you think of it as a data pipe, returning from write means that your data has entered the pipe, not that it has exited the pipe at the other end.
In fact, since the pipe is one where the data may take any of hundreds of different pathways, you're not even guaranteed that it will reach the other end :-) If that happens, you'll be notified about it at some later date, probably by a subsequent write failing.
It may be blocked:
trying to exit your machine due to a broken cable,
at a bottleneck in the path somewhere,
by the networking stack at the destination,
by a networking stack at some device in the networking path, more intelligent than a simple hub,
because the application at the other end is otherwise tied up,
and so on.
A successful return from write means that your local network stack has accepted your data and will process it in due course.
For various reasons, I am trying to download a CRL file using crude tools in C. I'm opening a tcp connection using good old socket(), sending a hardcoded plaintext http request via send(), reading the results into a buffer via recv(), and then writing that buffer into a file (which I will later use to verify various certs).
The recv() and write-to-file portions are inside a while loop so that I can get it all.
My problem is that I'm having a heck of a time coming up with a reliable means of determining when I'm done receiving the file (and therefore can break out of the while loop). Everything I've come up with so far has either had false positives or false negatives (getting back 0 bytes happens too frequently, and either the EOF marker wasn't there or I was looking in the wrong byte for it). Preferably, it would be a technique that wouldn't introduce a lot of additional complexity.
Really, I have a host, port, and a path (all as char*). On the far end, there's a friendly http server (though not one that I control). I'd be happy with anything that could get me the file without a large quantity of additional code complexity. If I had access to a command line, I'd go for something like wget, but I haven't found any direct equivalents over on the C API side, and system() is a poor choice for the situation.
'Getting back zero bytes', by which I assume you mean recv() returning zero, only happens when the peer has finished sending data and has closed the connection. Unless the peer is sending you multiple files per connection, this is an infallible sign of the end of this file. 'Too frequently' is nonsense: it can only happen once per connection.
But if the peer is an HTTP server it should be sending you a Content-length header. See RFc 2616.
Context: I'm developing a client-server application that is fairly solid most of the time, despite frequent network problems, outages, broken pipes, and so on. I use non-blocking sockets, select(), and OpenSSL to deliver messages between one or more nodes in a cluster, contingent on application-level heartbeats. Messages are queued and not removed from the queue until the entire message has been transferred and all the SSL_write()s return successfully. I maintain two sockets for each relationship, one incoming and one outgoing. I do this for a reason, and that's because it's much easier to detect a failed connection (very frequent) on a write than it is on a read. If a client is connecting, and I already have a connection, I replace it. Basically, the client performing the write is responsible for detecting errors and initiating a new connection (which will then replace the existing (dead) read connection on the server). This has worked well for me with one exception.
Alas, I'm losing messages. 99.9% of the time, the messages go through fine. But every now and then, I'll send, and I have no errors detected on either side for a few minutes... and then I'll get an error on the socket. The problem is that SSL_write has already returned successfully.
Let me guess: if I was blocking this would be fine, but since I'm non-blocking, I don't wait for the read on my remote end. As long as my TCP buffer can fit more, I keep stuffing things in the pipe. And when my socket goes poof, I lose anything in that buffer yet to be delivered?
How can I deal with this? Are application-level acks really necessary? (I'd rather not travel down the long road of complicated lost-acks and duplicate message complexity) Is there an elegant way to know what message I've lost? Or is there a way I can delay removal from my queue until I know it has been delivered? (Without an ack, how?)
Thanks for any help in advance.