views:

604

answers:

5

Hi!

I have a server and a client program (both running on the same machine). The client is able to send a struct to the server with members such as "ID", "size" etc. Then I would like the server to send the ID-member (just an integer) back to the client as an ACK for validation, but I just can't figure this out despite being able to send the struct without problems..

Here is the code from server.c:

/* having just recieved the struct */

int ACK_ID = struct_buffer->message_ID;
result = send(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if (result == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to send ACK.\n");
    exit(EXIT_FAILURE);
}

Here is the code from client.c:

// Recieve ACK from server
int ACK_ID;
com_result = read(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if ((com_result == -1) || (ACK_ID != metablocks[index].message_ID)) {
    printf("\n\t[ERROR] Failed to send metadata. ACK: %i\n", ACK_ID);
}

When I try to run this I get the following output from client.c:

[ERROR] Failed to send metadata. ACK: 14

And of course the server tells me it failed to send ACK. The value of the ID integer I'm trying to send should be 1, but it is recieved as 14. What am I doing wrong here?

Update
So I just tried what Mr. Shawley suggested, and got this error message:

Partial read: Undefined error: 0

First I tried exactly what he wrote, but then I noticed that the code is comparing com_result with sizeof(int). So I assumed that was a typo and tried replacing com_result with the ACK_ID variable in the comparison. Same result.

Update 2
Just added a perror() on the server when it fails, and got the following error message:

Bad file descriptor

I am using the same socket for this operation as the one I used when receiving the struct. Here is an expanded code sample from server.c:

// Recieve connection
CLIENT_socket = accept(SERVER_socket, (struct sockaddr *)&CLIENT_address, &CLIENT_address_length);
if (CLIENT_socket == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to accept client connection.\n");
    exit(EXIT_FAILURE);
}

printf("\n\tClient connected!\n");

int data_size;

// Read meta data from connection
data_size = sizeof(struct msg_meta);
result = read(CLIENT_socket, &meta_buffer_char, data_size, 0);
meta_buffer = (struct msg_meta *) meta_buffer_char;

if (result == -1) {
    close(SERVER_socket);
    printf("\n\t[ERROR] Failed to read from connection.\n");
    perror("\n\tRead");
    exit(EXIT_FAILURE);
} else if (result > 0) {
    printf("\n\tMessage recieved.\n");
    printf("\n");
}

// Send ACK back to client
int ACK_ID = meta_buffer->message_ID;
result = send(CLIENT_socket, &ACK_ID, sizeof(int), 0);

if (result == -1) {
    printf("\n\t[ERROR] Failed to send ACK.");
    perror("\n\tSend");
    printf("\n");
    close(SERVER_socket);
    exit(EXIT_FAILURE);
}

// Close sockets
close(SERVER_socket);
close(CLIENT_socket);
+1  A: 

You need to check the error that is being generated by the send. You should include the error library #include <cerrno> and then check the value of the global errno. There are a variety of macros that declare what type of error occurred. That will give you more information in debugging exactly why it failed to send the ACK.

Here's a list of the possible error values return (the error values are macros defined in cerrno), taken from here:

     [EACCES]           The SO_BROADCAST option is not set on the socket and a broadcast address is given as
                        the destination.

     [EAGAIN]           The socket is marked non-blocking and the requested operation would block.

     [EBADF]            An invalid descriptor is specified.

     [ECONNRESET]       A connection is forcibly closed by a peer.

     [EFAULT]           An invalid user space address is specified for a parameter.

     [EHOSTUNREACH]     The destination address specifies an unreachable host.

     [EINTR]            A signal interrupts the system call before any data is transmitted.

     [EMSGSIZE]         The socket requires that message be sent atomically, and the size of the message to
                        be sent makes this impossible.  IOV_MAX.

     [ENETDOWN]         The local network interface used to reach the destination is down.

     [ENETUNREACH]      No route to the network is present.

     [ENOBUFS]          The system is unable to allocate an internal buffer.  The operation may succeed when
                        buffers become available.

     [ENOBUFS]          The output queue for a network interface is full.  This generally indicates that the
                        interface has stopped sending, but may be caused by transient congestion.

     [ENOTSOCK]         The argument socket is not a socket.

     [EOPNOTSUPP]       socket does not support (some of) the option(s) specified in flags.

     [EPIPE]            The socket is shut down for writing or the socket is connection-mode and is no
                        longer connected.  In the latter case, and if the socket is of type SOCK_STREAM, the
                        SIGPIPE signal is generated to the calling thread.

     The sendmsg() and sendto() system calls will fail if:

     [EAFNOSUPPORT]     Addresses in the specified address family cannot be used with this socket.

     [EDESTADDRREQ]     The socket is not connection-mode and does not have its peer address set, and no
                        destination address is specified.

     [EISCONN]          A destination address was specified and the socket is already connected.

     [ENOENT]           A component of the pathname does not name an existing file or the path name is an
                        empty string.

     [ENOMEM]           Insufficient memory is available to fulfill the request.

     [ENOTCONN]         The socket is connection-mode, but is not connected.

     [ENOTDIR]          A component of the path prefix of the pathname in the socket address is not a direc-tory. directory.
                        tory.

     The send() system call will fail if:

     [EDESTADDRREQ]     The socket is not connection-mode and no peer address is set.

     [ENOTCONN]         The socket is not connected or otherwise has not had the peer pre-specified.

     The sendmsg() system call will fail if:

     [EINVAL]           The sum of the iov_len values overflows an ssize_t.

     [EMSGSIZE]         The socket requires that message be sent atomically, and the size of the message to
                        be sent makes this impossible, or the msg_iovlen member of the msghdr structure
                        pointed to by message is less than or equal to  or is greater than IOV_MAX.
Daniel Bingham
So you see no fault in the code itself?
Orolin
None that I can spot. But no error flag is rather strange. I'm as clueless as you are at this point. I'll keep looking at it...
Daniel Bingham
I mean, aside from the previously mentioned need to convert your integers to network byte ordering and back again.
Daniel Bingham
Could you please post a minimal source code which exhibits this problem?
Amigable Clark Kant
(A complete minimal test program I mean.)
Amigable Clark Kant
+1  A: 

My guess is that the read is failing resulting in com_result == -1. In this case, the value of ACK_ID is undefined stack garbage. Try this instead:

com_result = read(CLIENT_socket, &ACK_ID, sizeof(int), 0);
if (com_result < 0) {
    perror("read");
} else if (com_result != sizeof(int)) {
    /* handle partial read condition */
} else if (ACK_ID != metablocks[index].message_ID) {
    printf("\n\t[ERROR] Failed to receive metadata. ACK: %i\n", ACK_ID);
}

There are a number of reasons that read() might fail or return a partial result - this is TCP after all. Perror will essentially call strerror(errno) for you and display the message that you supply with the error string appended to it. When a system call like read() or send() returns -1, it sets errno to a more descriptive value that you can display using perror() or strerror().

Update - Partial Reads

As for the partial read problem, you usually solve this by either (1) ignoring it or (2) doing the read in a loop until you get all of the bytes that you are expecting. Something like:

int status = 0;
char *byte_ptr = (char*)&ACK_ID;
ssize_t bytes_left = sizeof(ACK_ID);
while (bytes_left > 0) {
   ssize_t rc = read(CLIENT_socket, byte_ptr, bytes_left);
   if (rc < 0) {
       if (errno == EINTR) {
           continue; /* interrupted system call */
       }
       perror("read");
       status = -1;
       break;
   } else if (rc == 0) {
       /* EOF */
       break;
   }
   bytes_left -= rc;
   byte_ptr += rc;
}
if (status == 0) {
    if (bytes_left == 0) {
        /* safely use the value stored in ACK_ID */
    } else {
        /* handle premature socket closure */
    }
}

Usually this is wrapped in a common library function to make life easier. I would recommend reading W. Richard Steven's UNIX Network Programming, volume 1 if you haven't already. This is exactly what he does in his readn() library function.

D.Shawley
Tried this and updated post.
Orolin
Partial read will not set errno, using perror there is fairly fruitless
Hasturkun
A: 

You could check why the send is failing. For example, use perror:

if (result == -1) {
    perror("server error while sending ack");
    ....

Be sure not to call any other function between the failed send and perror, as that would reset errno.

Andomar
Have tried this as suggested. Post is updated.
Orolin
But you still have perror() after other calls. Do it first.
Amigable Clark Kant
A: 

First, try using htonl before sending the int, and using ntohl after receiving it. Then, you should ALWAYS send and recv in loops, so you send and recv the WHOLE length of your data.

Spidey
+1  A: 

Re: update 2 - Make sure you are (a) using the correct file descriptor and (b) didn't close() or shutdown() the write portion of the socket.

Duck
Not completely sure what the problem was, but I'm guessing it had to be something I did wrong in closing the sockets. Have rewritten it now, and it works.
Orolin