0

I am writing a C software to interface with a motorcontroller over UDP. I'm using:

  • Win 7 Pro 64-Bit
  • eclipse luna
  • minGW

At the moment I have a problem, that seems to be socket/wsa related: the execution of my program gets stuck on the recvfrom() forever (so obviously the controller doesn't respond as expected). With the attached software this only happens on the first execution (or after not executing for ca. 3 mins) other programs had this problem 3-4 times in a row. A look into wireshark revealed, that the first (or after 3min pause) leads to the transmission of an "ARP" package instead of my UDP message. Why is that (it seems to be searching for the destination)? How can I avoid "crashing" my software due to this?

Did I forget to initialise anything?

My code looks like this:

#include <stdio.h>
#include <conio.h>
#include <winsock2.h>

#define Y_AXIS_IP "XXX.XXX.XXX.XX"
#define Y_AXIS_PORT 880X

int startWinsock(void);

int main() {
//Start the winsocket (needed to create sockets)
long ws = startWinsock();
if (ws) {
    printf("ERROR: Failed to init Winsock API! Code: %ld\n", ws);
    getch();
    exit(EXIT_FAILURE);
}
//Create an UDP Socket
SOCKET UDPsocket = socket(AF_INET, SOCK_DGRAM, 0);
if (UDPsocket == INVALID_SOCKET) {
    printf("ERROR: Socket could not be created! Code: %d\n",
            WSAGetLastError());
    getch();
    exit(EXIT_FAILURE);
}
//Create a struct to use with the socket (this gives information about type (AF_INET = Internet Protocol) which port and which IP to use)
SOCKADDR_IN addrY;
memset(&addrY, 0, sizeof(addrY));
addrY.sin_family = AF_INET; //Assert Type
addrY.sin_port = htons(Y_AXIS_PORT);    //Assert Port
addrY.sin_addr.S_un.S_addr = inet_addr(Y_AXIS_IP);  //assert IP Address

char message[] = "0000MTX     00000000OR:1:000F\r";

int buffersize = 100;
char *recvbuf = malloc(buffersize); //None of the replys can get larger than 100 chars

if (recvbuf == NULL) {
    printf("Out of memory!\n");
    getch();
    exit(EXIT_FAILURE);
}

//clear the receive buffer and prepare the address size
memset(recvbuf, '\0', buffersize);
int addrsize = sizeof(addrY);

//Send the message to the device
if (sendto(UDPsocket, message, strlen(message), 0,
        (struct sockaddr *) &addrY, sizeof(addrY)) == SOCKET_ERROR) {
    printf("sendto() failed with error code : %d", WSAGetLastError());
    getch();
    exit(EXIT_FAILURE);
}

//Receive from device (blocks program until recv event)
if (recvfrom(UDPsocket, recvbuf, buffersize, 0, (struct sockaddr *) &addrY,
        &addrsize) == SOCKET_ERROR) {
    //If not timed out Display the Error
    printf("recvfrom() failed with error code : %d", WSAGetLastError());
    getch();
    exit(EXIT_FAILURE);
}

printf("%s\n", recvbuf);
getch();

free(recvbuf);

return 0;
}

int startWinsock(void) {
WSADATA wsa;
return WSAStartup(MAKEWORD(2, 2), &wsa);
}

I would be really happy, if you had any ideas or suggestions. Thanks alot in advance!

whargarbel
  • 11
  • 4
  • This totally sounds like an ARP problem. Read about ARP. You need to handle the case where ARP will cause to drop the first packet after some idle time. – ElderBug Feb 13 '15 at 17:00

2 Answers2

0
  • Before a computer can send a packet to another host, it has to resolve its MAC address. This is done via an ARP request. It is handled by the operating system transparently.

  • recvfrom will block. It is not a bug. There are basically three ways of avoiding recvfrom from blocking forever when there is no incomming data:

    1. make the socket asynchronous, i.e. nonblocking
    2. set a timeout (see Set timeout for winsock recvfrom)
    3. use select (Setting winsock select timeout accurately)
Community
  • 1
  • 1
  • okay thank you, I have tried to add a static arp cache entry, but afterwards it is **always** ending in a recv timeout (thank you for the timeout suggestion) this is the command I use to set the static arp entry: arp -s "destination ip" "destination mac" "source ip" the transmission via sendto is now always successfull, but the recvfrom function never receives anything, do you have any further ideas, what I could try to fix that? – whargarbel Feb 17 '15 at 10:28
  • Are you sure that your you are receiving data? recvfrom will get data only if the peer has sent data. In wireshark, can you see any packets received before the timeout? If not, it might be a problem of the peer not sending any data. – Leonard Michlmayr Feb 19 '15 at 03:27
  • In wireshark it looks like there is no transmission (not even a sendto, although the sendto() is successfull [according to my program]), not a single byte gets transmitted over the ports, when the recvfrom ends in a timeout. So on every first execution, the sendto fails somehow and thus the recvfrom isn't able to receive anything. Is this a problem with the winsocket? – whargarbel Feb 20 '15 at 10:29
  • Is the peer in the same subnet and attached to the same link (LAN)? – Leonard Michlmayr Feb 20 '15 at 13:49
  • The computer and the motorcontroller are directly connected through a PCIe Card with multiple LAN Ports This one: http://ark.intel.com/de/products/50496/Intel-PRO1000-PT-Quad-Port-Server-Adapter – whargarbel Feb 20 '15 at 14:00
0

So finally I found a way to deal with this issue:

1. catch the timeout that occurs at recvfrom
2. save the sockets address in a temporary socket
3. close the original socket
4. end the WSA (call WSACleanup())
5. start the WSA (call WSAStartup())
6. create a new socket at the address of the former socket
7. transmit the message again

seems to work fine (also see code below)

If you see anything wrong or dangerous with my code, please feel free to comment and help my to improve my skills.

Thank you for your help and ideas,

Sebastian

Code:

if (recvfrom(*UDPsocket, buffer, buffersize, 0, (struct sockaddr *) &addr,
        &addrsize) == SOCKET_ERROR) {

    int WSAerror = WSAGetLastError();

    if (WSAerror == 10060) {

        printf("[WARNING] recvfrom() timed out!\n");
        //Store the address of the socket
        SOCKET *tempsocket = NULL;
        tempsocket = UDPsocket;
        //destroy the old socket
        closesocket(*UDPsocket);
        WSACleanup();
        //create a new socket
        startWinsock();
        *tempsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

        //Send the message to the device
        if (sendto(*tempsocket, Frame.pcompletemsg,
                strlen(Frame.pcompletemsg), 0, (struct sockaddr *) &addr,
                sizeof(addr)) == SOCKET_ERROR) {
            printf("[ERROR] sendto() failed with error code : %d\n",
                    WSAGetLastError());
            getch();
            WSACleanup();
            exit(EXIT_FAILURE);
        }
        if (recvfrom(*tempsocket, buffer, buffersize, 0,
                (struct sockaddr *) &addr, &addrsize) == SOCKET_ERROR) {

            int WSAerror = WSAGetLastError();
            printf("[ERROR] recvfrom() failed with error code : %d",
                    WSAerror);
            getch();
            WSACleanup();
            exit(EXIT_FAILURE);
        }

    } else {
        printf("[ERROR] recvfrom() failed with error code : %d", WSAerror);
        getch();
        WSACleanup();
        exit(EXIT_FAILURE);
    }
}
whargarbel
  • 11
  • 4