为什么recvfrom在选择进程后会返回错误的IP?

时间:2015-10-27 03:15:17

标签: c linux udp

我正在为UDP编写包装器。但是,如果在选择过程之后立即调用recvfrom,我的远程IP就出错了。而且它有时也会显示" recvfrom失败:无效的争论!" ......我已经挣扎了几个小时,但无法说明原因。

以下是源代码:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>

#ifndef _DEF_BYTE_
  #define _DEF_BYTE_
  typedef unsigned char  byte_t;  /*8 bits*/
#endif

#ifndef _DEF_WORD_
  #define _DEF_WORD_
  typedef unsigned short word_t;  /*16 bits*/
#endif

#ifndef _DEF_DWORD_
  #define _DEF_DWORD_
  typedef unsigned int  dword_t; /*32 bits*/
#endif

#define zTraceP(fmt, arg...) do{ \
  printf(fmt, ##arg); \
}while(0)

#ifndef MAX
#define MAX(a, b) ((a)>(b)?(a):(b))
#endif

typedef struct SOCKET_EX_TYPE
{ 
  int sock; 
  dword_t readable:1; 
  dword_t writable:1;
  dword_t exception:1;
  dword_t rsvd:29; 
} zSockEx_t;


int zCreateSocket(word_t lport)
{
  struct sockaddr_in  saddr; 
  int sock;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if(sock<0)
  {
    zTraceP("lc data socket @port %d error: %s\n", lport, strerror(errno));
    return -1;
  }

  memset(&saddr,0,sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(lport);
  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
  if(bind(sock,(struct sockaddr *)&saddr, sizeof (saddr))!=0)   
  {
    zTraceP("bind socket @port %d failed: %s!\n", lport, strerror(errno));
    return -1;
  }

  return sock;
}

int zSendto(int sock, dword_t rip, word_t rport, byte_t *buf, int len)
{
  int rc;
  struct sockaddr_in  saddr; 

  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family = AF_INET;
  rport = htons(rport);
  rip   = htonl(rip);
  saddr.sin_port = rport; 
  saddr.sin_addr.s_addr = rip;

  rc = sendto(sock, buf, len, 0, (const struct sockaddr*)(&saddr), sizeof(saddr));
  if(rc < 0)
  {
    zTraceP("Sendto @sock %d remote %08x:%d, len %d failed: %s!\n", sock, rip, rport, len, strerror(errno));
    return -1;
  }

  return rc;
}

int zTryRecvfrom(int sock, dword_t *rip, word_t *rport, byte_t *buf, int len, int sec, int usec)
{
  int rc;

  fd_set rfds;
  struct timeval tv;

  struct sockaddr_in  saddr; 
  int sin_len;

  FD_ZERO(&rfds);
  FD_SET(sock, &rfds);

  if(!sec && !usec)
  {
    tv.tv_sec = 0;
    tv.tv_usec = 500;
  }
  else
  {
    tv.tv_sec = sec;
    tv.tv_usec = usec;
  }

  rc = select(sock+1, &rfds, NULL, NULL, &tv);  
  if (rc < 0)
  {
    zTraceP("Recvfrom @sock %d failed: %s!\n", sock, strerror(errno));
    return -1;
  }

  if (rc)
  {
    if(FD_ISSET(sock, &rfds))
    {
      rc = recvfrom(sock, buf, len, 0, (struct sockaddr *)&saddr, (dword_t *)&sin_len);
      if(rc > 0)
      {
        if(rip) *rip = ntohl(saddr.sin_addr.s_addr);
        if(rport) *rport = ntohs(saddr.sin_port);

        return rc;
      }
    }
  }

  return 0;
}


int zEnumateSocket(int sec, int usec, zSockEx_t ex[], int count, int *nread, int *nwrite, int *nexception)
{
  int rc, i, mx;

  fd_set rfds, wfds, efds;


  struct timeval tv;  
  tv.tv_sec = sec;
  tv.tv_usec = usec;

  mx = 0;
  FD_ZERO(&rfds);
  FD_ZERO(&wfds);
  FD_ZERO(&efds);
  for(i=0; i<count; i++)
  {
    mx = MAX(ex[i].sock, mx);
    FD_SET(ex[i].sock, &rfds);
    FD_SET(ex[i].sock, &wfds);
    FD_SET(ex[i].sock, &efds);
  }

  rc = select(mx+1, &rfds, &wfds, &efds, &tv);
  if(rc == 0) return 0;

  if (rc < 0)
  {
    zTraceP("select failed: %s!\n", strerror(errno));
    return -errno;
  }

  int nr = 0; 
  int nw = 0;
  int ne = 0;
  for(i=0; i<count; i++)
  {
    if(FD_ISSET(ex[i].sock, &rfds)) { ex[i].readable = 1; nr ++; } else { ex[i].readable = 0; };
    if(FD_ISSET(ex[i].sock, &wfds)) { ex[i].writable = 1; nw ++; } else { ex[i].writable = 0; };
    if(FD_ISSET(ex[i].sock, &efds)) { ex[i].exception= 1; ne ++; } else { ex[i].exception= 0; };
  }
  if(nread) *nread = nr;
  if(nwrite) *nwrite = nw;
  if(nexception) *nexception = ne;

  return rc;
}

int zRecvfrom(int sock, dword_t *rip, word_t *rport, byte_t *buf, int len)
{
  int rc;  

  struct sockaddr_in  saddr; 
  int sin_len;

  rc = recvfrom(sock, buf, len, 0 /*MSG_DONTWAIT*/, (struct sockaddr *)&saddr, (dword_t *)&sin_len);
  if(rc == 0) return 0;

  if (rc < 0)
  {
    zTraceP("recvfrom failed: %s!\n", strerror(errno));
    return -errno;
  }

  if(rip) *rip = ntohl(saddr.sin_addr.s_addr);
  if(rport) *rport = ntohs(saddr.sin_port);

  return rc;
}


int zShutdownSocket(int sock)
{
  shutdown(sock, SHUT_RDWR);

  return 0;
}

int zCloseSocket(int sock)
{
  close(sock);

  return 0;
}


#define SALNET_SELFTEST 1

#ifdef SALNET_SELFTEST

int main(int argc, char** argv) //int testSalNet()
{
  char* str = "Hello, from 1025";
  byte_t buf[100];

  int rc;
  zSockEx_t ex[2];
  int port0=10251;
  int port1=10261;

  dword_t rip = 0;
  word_t rport = 0;

  int nread, nwrite, nexception;
  ex[0].sock = zCreateSocket(port0);
  ex[1].sock = zCreateSocket(port1);

  rc = zSendto(ex[0].sock, 0x7F000001, port1, (byte_t*)str, strlen(str)+1);
  zTraceP("send to, rc=%d\n", rc);
  if(rc <= 0) return -1;

  rc = zEnumateSocket(1, 100, ex, 2, &nread, &nwrite, &nexception);
  zTraceP("enumate rc=%d, read %d, write %d, exception %d\n", rc, nread, nwrite, nexception);
  if(rc < 0) return -1;

  rc = zRecvfrom(ex[1].sock, &rip, &rport, buf, sizeof(buf));
  zTraceP("recvfrom rc=%d, remote %08x:%d\n", rc, rip, rport);
  if(rc <= 0) return -1;  

  rc = zSendto(ex[0].sock, 0x7F000001, port1, (byte_t*)str, strlen(str)+1);
  zTraceP("send to, rc=%d\n", rc);
  if(rc <= 0) return -1;

  rc = zTryRecvfrom(ex[1].sock, &rip, &rport, buf, sizeof(buf), 0, 100);
  zTraceP("try recvfrom rc=%d, remote %08x:%d\n", rc, rip, rport);
  if(rc <= 0) return -1;

  rc = zEnumateSocket(0, 100, ex, 2, &nread, &nwrite, &nexception);
  zTraceP("enumate rc=%d, read %d, write %d, exception %d\n", rc, nread, nwrite, nexception);
  if(rc < 0) return -1;


  rc = zSendto(ex[0].sock, 0x7F000001, port1, (byte_t*)str, strlen(str)+1);
  zTraceP("send to, rc=%d\n", rc);
  if(rc <= 0) return -1;

  rc = zRecvfrom(ex[1].sock, &rip, &rport, buf, sizeof(buf));
  zTraceP("recvfrom rc=%d, remote %08x:%d\n", rc, rip, rport);
  if(rc <= 0) return -1;  

  rc = zSendto(ex[0].sock, 0x7F000001, port1, (byte_t*)str, strlen(str)+1);
  zTraceP("send to, rc=%d\n", rc);
  if(rc <= 0) return -1;

  rc = zRecvfrom(ex[1].sock, &rip, &rport, buf, sizeof(buf));
  zTraceP("recvfrom rc=%d, remote %08x:%d\n", rc, rip, rport);
  if(rc <= 0) return -1;  


  zCloseSocket(ex[0].sock);
  zCloseSocket(ex[1].sock);

  return 0;
}

#endif /*SALNET_SELFTEST*/

以下是我的测试结果:

->testSalNet()

$1/> testSalNet()

send to, rc=17
enumate rc=1, read 1, write 0, exception 0
[Error]:salNet.c zRecvfrom 174::recvfrom failed: Invalid argument!
recvfrom rc=-22, remote 00000000:0
 = -1 (0xFFFFFFFFFFFFFFFF)
->q

->testSalNet()

$2/> testSalNet()

send to, rc=17
enumate rc=3, read 1, write 2, exception 0
recvfrom rc=17, remote 01000000:0
send to, rc=17
try recvfrom rc=17, remote 7f000001:10251
enumate rc=2, read 0, write 2, exception 0
send to, rc=17
recvfrom rc=17, remote 7f000001:10261
send to, rc=17
recvfrom rc=17, remote 7f000001:10261
 = 0 (0x0)
->q

1 个答案:

答案 0 :(得分:0)

您必须先将sin_len设置为sizeof(saddr) ,然后再致电recvfrom()