C windows 套接字在第一次执行时永远阻塞
C windows socket forever blocked on first execution
我正在编写一个 C 软件以通过 UDP 与电机控制器进行交互。
我正在使用:
- Win 7 专业版 64 位
- 日食月神
- minGW
目前我有一个问题,似乎与 socket/wsa 有关:
我的程序的执行永远停留在 recvfrom() 上(所以显然控制器没有按预期响应)。
使用附带的软件,这只会在第一次执行时发生(或在未执行大约 3 分钟后),其他程序连续 3-4 次出现此问题。
查看 wireshark 发现,第一个(或暂停 3 分钟后)导致传输 "ARP" 包而不是我的 UDP 消息。
这是为什么呢(好像是在寻找目的地)?我该如何避免 "crashing" 我的软件因此而受到影响?
我是不是忘记初始化什么了?
我的代码如下所示:
#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, '[=11=]', 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);
}
如果您有任何想法或建议,我将非常高兴。提前致谢!
在计算机可以将数据包发送到另一台主机之前,它必须解析其 MAC 地址。这是通过 ARP 请求完成的。它由操作系统透明地处理。
recvfrom 会阻塞。这不是错误。当没有传入数据时,基本上有三种方法可以避免 recvfrom 永远阻塞:
- 使套接字异步,即非阻塞
- 设置超时(参见 Set timeout for winsock recvfrom)
- 使用 select (Setting winsock select timeout accurately)
所以我终于找到了解决这个问题的方法:
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);
}
}
我正在编写一个 C 软件以通过 UDP 与电机控制器进行交互。 我正在使用:
- Win 7 专业版 64 位
- 日食月神
- minGW
目前我有一个问题,似乎与 socket/wsa 有关: 我的程序的执行永远停留在 recvfrom() 上(所以显然控制器没有按预期响应)。 使用附带的软件,这只会在第一次执行时发生(或在未执行大约 3 分钟后),其他程序连续 3-4 次出现此问题。 查看 wireshark 发现,第一个(或暂停 3 分钟后)导致传输 "ARP" 包而不是我的 UDP 消息。 这是为什么呢(好像是在寻找目的地)?我该如何避免 "crashing" 我的软件因此而受到影响?
我是不是忘记初始化什么了?
我的代码如下所示:
#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, '[=11=]', 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);
}
如果您有任何想法或建议,我将非常高兴。提前致谢!
在计算机可以将数据包发送到另一台主机之前,它必须解析其 MAC 地址。这是通过 ARP 请求完成的。它由操作系统透明地处理。
recvfrom 会阻塞。这不是错误。当没有传入数据时,基本上有三种方法可以避免 recvfrom 永远阻塞:
- 使套接字异步,即非阻塞
- 设置超时(参见 Set timeout for winsock recvfrom)
- 使用 select (Setting winsock select timeout accurately)
所以我终于找到了解决这个问题的方法:
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);
}
}