C windows套接字在第一次执行时永远被阻止

时间:2015-02-13 16:33:03

标签: c windows sockets winsock

我正在编写一个C软件,通过UDP与电机控制器连接。 我正在使用:

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

目前我遇到了问题,这似乎与socket / wsa有关: 我的程序的执行永远停留在recvfrom()上(显然控制器没有按预期响应)。 使用附加的软件,这只发生在第一次执行时(或者在约3分钟后未执行)其他程序连续3-4次出现此问题。 看看wireshark透露,第一个(或暂停3分钟后)导致传输“ARP”包而不是我的UDP消息。 为什么(似乎是在寻找目的地)?我怎样才能避免因此而“崩溃”我的软件?

我忘记初始化了吗?

我的代码如下所示:

#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);
}

如果您有任何想法或建议,我会非常高兴。非常感谢!

2 个答案:

答案 0 :(得分:0)

  • 在计算机将数据包发送到另一台主机之前,它必须解析其MAC地址。这是通过ARP请求完成的。它由操作系统透明地处理。

  • recvfrom会阻止。这不是一个bug。当没有数据出现时,基本上有三种方法可以避免recvfrom永远阻塞:

    1. 使套接字异步,即非阻塞
    2. 设置超时(请参阅Set timeout for winsock recvfrom
    3. 使用select(Setting winsock select timeout accurately

答案 1 :(得分:0)

所以最后我找到了解决这个问题的方法:

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

似乎工作正常(另见下面的代码)

如果您发现我的代码有任何错误或危险,请随时发表评论并帮助我提高自己的技能。

感谢您的帮助和想法,

塞巴斯蒂安

代码:

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);
    }
}