views:

1474

answers:

4

I'm trying to make a TCP Client program in C where the client will start up, connect to a server. Then it will send a little information and then just listen to what it receives and react accordingly.

The part that I'm having trouble with is the continuous listening. Here is what I have

...

while (1) {
   numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0);
   buf[numbytes] = '\0';
   printf("Received: %s\n", buf);
   // more code to react goes here
}

...

Upon connecting to the server, after sending two lines of data, the server should receive a good bit of information, but when I run this, it prints:

Received:

And then continues to just sit there until i force it to close.

If anyone could offer some help, that would be great.

Thanks

** EDIT ** when i do what Jonathan told me to do, I get the following:

Count: -1, Error: 111, Received:

So that means its erroring, but what do i do about it?

+3  A: 

Print out the number of bytes received - it is likely to be zero, but confirm that.

It would be worth checking that you aren't getting an error - and therefore underflowing your buffer.

[Note: from here onwards is the work of Pax - thank you, and I've converted it to Community Wiki so I don't get rep points undeservedly.]

The following code will do this. Try it and report back on the results, please.

while (1) {
    numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0);
    buf[numbytes] = '\0';
    printf("Count: %d, Error: %d, Received: %s\n", numbytes, errno, buf);
    // more code to react goes here
}

After question edit:

Error number 111 is ECONNREFUSED - this is not a usual error code for recv(), but is more suited to the open-type call (open(), connect(), etc).

In any case, ECONNREFUSED is a problem at the server end, not the client - the server has purposefully refused to accept your incoming connection, so you will need to investigate that end of the link.

In order to test this, change your code so that it's connecting to www.microsoft.com on port 80, then send a couple of lines of any old rubbish. You should get back an error from their web server indicating a malformed HTTP request. This will prove there's no problem on your client end.

This is what I get back when I telnet www.microsoft.com 80 and type in hello followed by ENTER twice:

HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Thu, 27 Nov 2008 01:45:09 GMT
Connection: close
Content-Length: 326

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"&gt;
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request - Invalid Verb</h2>
<hr><p>HTTP Error 400. The request verb is invalid.</p>
</BODY></HTML>

You should see something similar.

Jonathan Leffler
When recv returns 0 it indicates the socket was closed by the remote host, so it probably isn't 0. It might be -1, indicating an error, and the error might be EWOULDBLOCK or EAGAIN indicate the non-blocking read failed because there's no data to receive.
SoapBox
You're welcome, @JL, I'd rather modify a half-decent answer than try to pinch rep, especially when your response was correct - all I did after that was flesh out some options for the questioner to investigate.
paxdiablo
+2  A: 

I strongly recommend Beej's Guide to Network Programming.

This section in particular has code for a client which does exactly what you ask.

Claudiu
this is where i got the code that i have, and as far as I can tell, it does not actually loop the receiving action. Just receives once.
The.Anti.9
A: 

EDIT: the answer below was based on a misunderstanding of the question - the OP's code is actually trying to recv() on a socket that it open()ed to a remote server. Stuff below left for posterity.


Please show more of your code.

A few observations though:

  • is the socket listen()ing?
  • have you accept()ed the incoming connection
  • it's slightly unusual to use recv() on a TCP socket. Use read() instead.
  • use strerror() to convert the 111 error code into the local error string - each UNIX O/S can have its own mapping from numbers to Exxx error codes so we can't tell what this error is on your system.

The normal code loop (for a single threaded non-forking app) looks like:

s = socket();
err = listen(s, n); // n = backlog number
while (1) {
    int fd = accept(s, &addr, sizeof(addr));
    while (1) {
        int numrecv = read(fd, ...);
        // break if eof
    }
    close(fd);
}
close(s);
Alnitak
Why is it unusual to use recv on a socket?
Graeme Perrow
ok, _slightly_ unusual. Unless you need socket specific flags I prefer to use a normal 'read' function so that any type of file descriptor can be used.
Alnitak
I'm not trying to Listen for incoming connections. This is a client.
The.Anti.9
ah - ok - misunderstood the messsage flow. In which case your 111 is probably ECONNREFUSED, meaning that the other end didn't accept the connection. Check the result of your connect() system call.
Alnitak
A: 

What's with the infinite loop? Why not use select() so that you only call recv() when there's actual data to be read?

In a former life I wrote networking code for a MUD, and I can still write the polling loop in my head. The loop in ROM 2.3 went something like this (from memory, so forgive me if the macro parameters are in the wrong order):

#define MAX_CONNECTIONS 256
int main(int argc, char *argv[])
{
  int i = 0;
  int running = 1;
  int connections[MAX_CONNECTIONS];
  while( running )
  {
    fd_set in_fd, out_fd, exc_fd;
    FD_ZERO(in_fd);
    FD_ZERO(out_fd);
    FD_ZERO(exc_fd);
    for( i = 0; i < MAX_CONNECTIONS; i++ )
    {
      if( connections[i] > 0 )
      {
        FD_SET(&in_fd, connections[i]);
        FD_SET(&out_fd, connections[i]);
        FD_SET(&exc_fd, connections[i]);
      }
    }
    select(&in_fd, &out_fd, &exc_fd, NULL); // this will block until there's an I/O to handle.
    for( i = 0; i < MAX_CONNECTIONS; i++ )
    {
      if( FD_ISSET(&exc_fd, connections[i]) )
      { /* error occurred on this connection; clean up and set connection[i] to 0 */ }
      else
      {
        if( FD_ISSET(&in_fd, connections[i]) )
        { /* handle input */ }
        if( FD_ISSET(&out_fd, connections[i]) )
        { /* handle output */ }
      }
    }
  }
}
Nathan Strong