transfer integer over a socket in C

54,403

Solution 1

First of all, sizeof(int) may differ on your sender and receiver machine. So I would recommend you to use something like int32_t from stdint.h.

Also, it is not guaranteed that read(..,..,sizeof(int)) will read exactly sizeof(int) bytes - it can read nothing, or it can read less bytes. So, the correct variant will be something more like this:

int send_int(int num, int fd)
{
    int32_t conv = htonl(num);
    char *data = (char*)&conv;
    int left = sizeof(conv);
    int rc;
    do {
        rc = write(fd, data, left);
        if (rc < 0) {
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                // use select() or epoll() to wait for the socket to be writable again
            }
            else if (errno != EINTR) {
                return -1;
            }
        }
        else {
            data += rc;
            left -= rc;
        }
    }
    while (left > 0);
    return 0;
}

int receive_int(int *num, int fd)
{
    int32_t ret;
    char *data = (char*)&ret;
    int left = sizeof(ret);
    int rc;
    do {
        rc = read(fd, data, left);
        if (rc <= 0) { /* instead of ret */
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                // use select() or epoll() to wait for the socket to be readable again
            }
            else if (errno != EINTR) {
                return -1;
            }
        }
        else {
            data += rc;
            left -= rc;
        }
    }
    while (left > 0);
    *num = ntohl(ret);
    return 0;
}

Solution 2

This should work without any problem, try this :

On the sender (Server) side :

int number_to_send = 10000; // Put your value
int converted_number = htonl(number_to_send);

// Write the number to the opened socket
write(client_socket, &converted_number, sizeof(converted_number));

On the receiver(client) side :

int received_int = 0;

return_status = read(client_socket, &received_int, sizeof(received_int));
if (return_status > 0) {
   fprintf(stdout, "Received int = %d\n", ntohl(received_int));
}
else {
   // Handling erros here
}

Hope this will help.

Solution 3

I also had the same problem and wasted 30 minutes trying to find out what I did wrong. But, finally, I found the solution. From the documentation we can see that htonl() and ntohl() functions work with 32 bit unsigned integers. So, to fix the issue, you have to use unsigned __int32 or uint32_t from <stdint.h>.

So the code will look like this:

#include <stdint.h>

uint32_t n = 4;
uint32_t tmp = htonl(n);
write(socket, &tmp, sizeof(tmp));

and:

#include <stdint.h>

uint32_t tmp,n;
read(socket, &tmp, sizeof(tmp));
n = ntohl(tmp);

Solution 4

Since no one mentioned sprintf

you can just convert any variable to char* using it and send

if(strcmp(buf,"movUP") == 0)
{
    char* msg = calloc(1, 20);
    pos.y += 0.0001f;
    sprintf(msg,"NEW::POS::Y=%.4f", pos.y);
    sendto(master, msg, 20, 0, (struct sockaddr*)&client, addrlen);
}

Test

movUP
NEW::POS::Y=0.0001
movUP
NEW::POS::Y=0.0002
movUP
NEW::POS::Y=0.0003
movUP
NEW::POS::Y=0.0004

Use %d for integers, %f for floats

to convert back to an integer, use atoi(char*)
to convert back to an float, use atof(char*)

before converting, be sure to use strstr() to get the float value only, starting from "0"

    float myPos; // floating variable that stores Object's Position in the World
    ...
    ....
    memset(buf, 0, MAXBUFFER); // clears the previous buffer
    recvfrom(master, buf, MAXBUFFER, 0, (struct sockaddr*)&server, &addrlen);
    char* newY = strstr(buf, "0");// NEW::POS::Y=0.0001 --->> 0.000100
    myPos = atof(newY); // new object's position by the server
    printf("My New Position is %.4f\n", myPos); // Out: My New Position is 0.0011 -> 0.0012 -> 0.0013 -> 0.0014.

For integers (not positions), you can use the same technique and just multiply it like

float f = 0.000002f; // that is supposed to be 2 integer value
int i = (int)(f*1000000); // now, i = 2

the above methods are totally secure

If you want a more solid converting, you can use strncpy or memcpy and cut the string starting from a given index with some length assuming that you already know the incoming buffer, but in my personal view, I don't really recommend it, specifically in connectionless sockets like this one, lots of calculations and calls for buffer length, Not easy to debug sometimes if you're not totally aware of what you're doing.

Note 1: Be careful to not include zeros in your buffer when you're waiting for a server move/position command or any quantity variable if you're planning to use the 1st method.

Note 2: You can just send your integer or float, convert it and vice versa without needing to cut or multiply it.

May new game networking developers find this answer useful too since we can't send or receive except char* with UDP sendto(), recvfrom().

Share:
54,403
Maggie
Author by

Maggie

Skilfull with creating bugs. Occasionally produces useful lines of code.

Updated on October 15, 2020

Comments

  • Maggie
    Maggie over 3 years

    What is the appropriate way to transfer an int over a socket in C?
    What I am doing so far is:

    int n = 4;
    int tmp = htonl(n);
    write(socket, &tmp, sizeof(tmp));
    

    and

    int tmp,n;
    read(socket, &tmp, sizeof(tmp));
    n = ntohl(tmp);
    

    However, the integer received is sometimes 0. Not always, but let's say 2 out of 5 times. It is never some other value, always 0. Why?

    UPDATE: Return value from read is -1 and an error is:

    Resource temporarily unavailable
    
  • Borisko
    Borisko over 12 years
    >UPDATE: Return value from read is -1. Do you use nonblocking IO?
  • garlix
    garlix over 8 years
    I think that the receive function is totally wrong. IMHO you used ret instead of rc that is actually never used
  • garlix
    garlix over 8 years
    This should be the accepted answer! Thanks! +1 for htonl and ntohl
  • michelson
    michelson over 7 years
    Also you should never look at errno unless read returned -1. You are inspecting it when 0 is returned which is going to lead to a bug that is really painful to track down
  • michelson
    michelson over 7 years
    But this does not handle partial reads or writes! read can return the result one byte at a time.
  • Carey Gregory
    Carey Gregory over 7 years
    @D.Shawley You're right. This answer has the same problem as the question and solves nothing. You should downvote it for that reason.
  • noooooooob
    noooooooob over 6 years
    @garlix is right . Yes! In receive_int(), it should be 'if (rc <= 0) {' instead of 'if (ret <= 0) {'!!