0

我正在尝试编写一个非常简单的 C 程序。出于某种原因,我不能在同一个程序中使用 writeFileEx 和 recvfrom,因为每次调用 writeFileEx 都会导致 recvfrom 崩溃。

这是相关的代码片段

struct sockaddr_in server, client_address;
int client_length, recv_len;
char buf[BUFLEN];
WSADATA wsa;
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0 )
{
  //Error
}

SOCKET s = socket (AF_INET, SOCK_DGRAM, 0);
if (s == INVALID_SOCKET)
{
 //invalid socket error
}

server.sin_family = AF_INET;
server.sin_addr.s_addr= INADDR_ANY;
server.sin_port = htons (PORT);

if (bind(s, (stuct sockaddr* ) &server, sizeof(server)) == SOCKET_ERROR)
{
  //bind error
}

OVERLAPPED ovWrite;
memset(&ovWrite,0,sizeof(ovWrite));
ovWrite.offset=0;
ovWrite.OffsetHigh=0;
ovWrite.hEvent = CreateEvent (0,TRUE,0,0);

memset(buf,'\0',BUFLEN);


while (1) 
{
  if (!recvfrom(s, buf, BUFLEN,0,(struct sockaddr *) &client_address, &client_length)
  {
    fprintf(stderr, "Recvfrom Failed %d\n", WSAGetlastError());
  }

  if (!WriteFileEx(serialHandle, buf, strlen(buf),&ovWrite,NULL))
  {
     fprintf(stderr, "Error writing to COM port %d\n", GetlastError());
  }
   memset(buf,'\0', BUFLEN);
}

我正在使用 lcc 编译器,结果程序在循环的第二次迭代中崩溃,访问冲突 0xc0000005。当我用另一个输入源(如标准输入)替换 recvfrom 时,程序运行良好。这是一个已知问题还是 2 个函数调用根本不能同时存在?

4

2 回答 2

0

recvfrom()不添加 a '\0'to buf,因此 at least() 如果recvfrom()读取BUFLEN字节,或者在第一次调用时,如果buf未在代码片段之外初始化,则调用strlen()会导致 UB。此外,你if( recvfrom( ... ) )错了,因为在成功的情况下,`recvfrom() 返回接收到的字节数,所以条件为真。相反,您可以这样做:

size_t received;

....

if ((received = recvfrom(s, buf, BUFLEN,0,(struct sockaddr *) &client_addres, &client_length)) <= 0 )
{
     fprintf(stderr, "Recvfrom Failed %d\n", WSAGetlastError());
}

if (!WriteFileEx(serialHandle, buf, received,&ovWrite,NULL))
{
     fprintf(stderr, "Error writing to COM port %d\n", GetlastError());
}
于 2013-08-09T10:16:33.883 回答
0

好的,我会宣传自己来提供答案:) 几点:

1) 与 WASRecv 不同,WriteFileEx() 总是需要一个有效的完成例程来回调异步写入调用的完成。其他信令机制,如使用 hEvent 同步,不适用于此调用。传递 NULL 可能会导致 AV。

2) 一般情况下,UDP 消息可以是 0..64K 的任意长度。recvFrom() 返回此类消息的实际长度,并且应该使用此值来处理它 - 不是任何 BUFFER_LEN 或 strlen() 调用,(请参阅 Ingo 答案:)。

3) IP 堆栈可以同时处理来自一个线程的阻塞读取和来自另一个线程的写入 - 并非绝对需要求助于异步、重叠的 I/O。

4) 如果要使用重叠 I/O,强烈建议每次调用使用不同的 OVL 块和缓冲区 - 通常,多个重叠操作可以同时排队,并且它们必须各自有自己的缓冲区/OVL。我使用 malloced 结构(或者,如果是 C++,则为新的类实例)或从池中分配的 *struct 来确保每次调用。结构/实例可以在处理后在完成例程中馈送/删除/重新池化,(或从线程排队并稍后在其他地方处理/释放/删除/重新池化)。

5) 希望使用完成例程执行重叠 I/O 的线程应该看起来像一个围绕可警报等待调用的循环。WaitForSingleObjectEx() 很方便,因为它可以以可提醒的方式等待构成生产者-消费者队列基础的信号量,从而允许其他线程向执行重叠 I/O 的线程发送命令/缓冲区/任何内容。

于 2013-08-09T12:03:22.157 回答