Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

recv()

Status
Not open for further replies.

sedj

Programmer
Aug 6, 2002
5,610
Hi,

I asked this question ages ago, and got around the problem by looking for a special terminating charcter or set of characters, but I'd like to revisit it, as I can't quite believe that what I'm seeing really the case.

Posted below is a very simple programme that listens on a port, and prints out the request. There is purposely no error checking on lots of the function calls, in order to keep things as simple as possible for those reading this post.

My question is, how do you tell when a client has sent all the data it wants to send ? It seems to just hang on the blocking recv(). Using fcntl() to set non-blocking seems to just break things.

Can anyone tell me whats going on or where my mistake lies, or is this correct behaviour ?
[Coming from a Java background I find this a bit odd, as the java.net.Socket classes and the associated IO streams don't seem to block when doing a similar kind of thing].

To test it, just do a "wget and watch the blocking happen in recvFunc() !

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8080

void recvFunc(int client_sd);

int main(void) {
	// set up the socket stuff
    struct sockaddr_in name;
	int server_sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	int yes = 1;
	setsockopt(server_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
	memset(&name, 0, sizeof(name));
	name.sin_family = AF_INET;
	name.sin_port = htons(PORT);
	name.sin_addr.s_addr = htonl(INADDR_ANY);
	bind(server_sd, (struct sockaddr *)&name, sizeof(name));
	listen(server_sd, 10);

	// accept loop
	while (1) {
		int client_sd;
		struct sockaddr_in client_addr;
		socklen_t sin_size = sizeof(client_addr);
		client_sd = accept(server_sd, (struct sockaddr *) &client_addr, &sin_size);

		recvFunc(client_sd);

	}
}

void recvFunc(int client_sd) {
	//fcntl(client_sd, F_SETFL, O_NONBLOCK);

	int read;
	char data;
	while (1) {
		// just read a byte at a time to keep things simple
		read = recv(client_sd, &data, 1, 0);

		printf("%c", data);

		if (read == 0) {
			printf("\0 bytes read - breaking\n");
			break;
		}

		if (read < 0) {
			perror("recv broke :");
		}

	}

	printf("\nrecvFunc() complete\n");

}

Thanks for any insights.

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
I tried it. It works the way I'd expect it to.

You know a client is done sending data when the other end [tt]close[/tt]s or [tt]shutdown[/tt]s its end of the connection. When that happens, your program's call to [tt]read[/tt] returns 0 bytes, just as if it had hit EOF or someone closed a pipe.

With your example, just Ctrl-C the [tt]wget[/tt] and it closes its side, and your program notices, breaks, prints the "recvFunc() complete" message, and accepts a new connection.

You might not be noticing because you have a NUL at the beginning of your "0 bytes read - breaking" message. That's a neat little trick I'll have to remember for "commenting" out a string, but it's probably not what you were going for. ;-)
 
oops, meant \n rather than \0.

Yes, Ctrl-C on the wget does cause the prog to break out - but thats not really the desired way - ideally, the prog should know that the client has sent all the data it has to send, and so should break out without the socket being closed down.
I know I'm thinking in terms of Java, where all the recv() and shenanigans is wrapped for you, but there must be a way to break out of recv without 1) quitting the socket and 2) looking for some kind of terminating character.

Or is there not ?

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
ideally, the prog should know that the client has sent all the data it has to send
That's just it. In the wget example, wget hasn't sent all the data it's going to send. It's waiting for your program to reply to it. Wget isn't "finished" until your program uses HTTP to do what it wants or tell it to go away, or it times out (try it with the --timeout option), or you tell it to give up with Ctrl-C.


Have you ever telnetted to, for example, a SMTP server, to learn about the protocol, and manually typed messages to the server? I bet the server thought you were slow for a program, but it probably kept listening until you were done talking to it.

Until you closed the connection (or asked that the server close it), it was alive, even though you weren't sending anything right then.

The server, meanwhile, was trying to read from the socket, and the [tt]read[/tt] kept blocking. The whole purpose of the blocking is to keep [tt]read[/tt] from returning until it's read something; otherwise, it could return zero before the connection closed. Theonly time it can ever return zero is when the other end closes the connection.

Reading from a socket is conceptually no different from reading from a file. When you read a file and [tt]read[/tt] returns 0, that means it hit EOF. When reading from a socket, a zero return means it hit end of "connection" or end of data stream, which is the same thing as end of file, as far as the logic is concerned.


there must be a way to break out of recv without 1) quitting the socket and 2) looking for some kind of terminating character.
You could have your program use [tt]select(2)[/tt] to poll the socket for incoming data (which is useful when you're a real server and are listening on multiple sockets). That'll also let you stick a timeout on it so you can close a connection if you decide it's really become idle.

In general, though, you have to wait until the other end closes the connection to know it's done.


If you're still confused, lemme see a Java example. Maybe I can see what you're thinking.
 
I'm losing it I think.

Java does behave exactly the same after all !

The example I thought behaved differenly does a read on the stream and does actually check for a terminating character. After going back to find it, I thought - Doh !

Thanks for the replies, its finally clarified something that has been bugging me in the background - basically, TCP/IP has no terminating character, but each layered protocol (HTTP, FTP etc) does - and this is what a programme must use in order to determine when to stop recv()'ing.

Damn my Java background for its fuzzy layers confusing my feeble mind :p

--------------------------------------------------
Free Java/J2EE Database Connection Pooling Software
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Sponsor

Back
Top