【问题标题】:problems sending structure UDP Socket发送结构 UDP Socket 的问题
【发布时间】:2021-07-04 14:53:40
【问题描述】:

我是 C++ 新手,需要帮助。 我使用 UDP 服务器接收结构,但是我无法读取它,客户端发送一个我调用的结构:ChannAccessReq 所以发送结构,服务器使用 RECVFROM 接收它,我使用通用结构通过仅读取结构的标头(H1),然后在条件已满时使用缓冲区更精确的结构(temp2)进行读取。 但是客户端需要发送两次消息,第一次发送到 recvfrom,第二次发送到 read()(我认为)我试了一整天,想知道缓冲区的大小是多少?

我认为最敏感的部分是在带有 recvfrom() 的服务器中,它的结构与紧随其后的 read() 不同..

希望清楚!

这里是服务器:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "struct.h"
#include <iostream>
void DieWithError(char *err) {
   perror(err);
   exit(1);
}

typedef struct IntMsgHeaderType {
  uint8_t code ;      // Code message7
  uint8_t bourrage ;  // Octet de bourrage
  uint16_t ParamLength; // Longueur eventuel données complémentaires

} HeaderInt;

typedef struct TextMessage //TESTTESTTEST
{
     HeaderInt H;      // Code message7


};

int main(int argc, char *argv[])
{
     int sock;                        /* Socket */
     struct sockaddr_in echoServAddr; /* Local address */
     struct sockaddr_in echoClntAddr; /* Client address */
     unsigned int cliAddrLen;         /* Length of incoming message */
     unsigned short echoServPort;     /* Server port */
     int recvMsgSize;                 /* Size of received message */

   struct TextMessage * temp = (TextMessage *)malloc(sizeof(struct TextMessage));
   HeaderInt *H1 =(HeaderInt *)malloc(104+sizeof(HeaderInt));
    ChanAccesReq *temp2=(ChanAccesReq *)malloc(sizeof(ChanAccesReq));
  if (!argv[1]) {
      fprintf(stderr,"no port number provided");
     exit(1);
  }

      echoServPort = atoi(argv[1]);  /* First arg:  local port */

    /* Create socket for sending/receiving datagrams */
  if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    DieWithError("socket() failed");

  /* Construct local address structure */
  memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
  echoServAddr.sin_family = AF_INET;                /* Internet address family */
  echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
  echoServAddr.sin_port = htons(echoServPort);      /* Local port */

  /* Bind to the local address */
  if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
     DieWithError("bind() failed");

  for (;;) /* Run forever */
  {

         cliAddrLen = sizeof(echoClntAddr);
         int nbrOctet;

         if (recvfrom(sock, H1, sizeof(*H1), 0,(struct sockaddr *) &echoClntAddr, &cliAddrLen)>0 && H1->code==1){


         //read(sock,H1,sizeof(*H1));
         std::cout<<"taille nbrOctet : "<<nbrOctet<<'\n';
         memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */

         read(sock, temp2, sizeof(*temp2));
                //read(sock,temp2,sizeof(*temp2))>0;
                    std::cout<<unsigned(temp2->P.linkAddr)<<'\n';

        };

   }

        close(sock);
        return 0;
  }

这里是客户

#include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <string.h>
  #include <strings.h>
  #include <arpa/inet.h>
  #include <netinet/in.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include "struct.h"

  void DieWithError(char *err) {
       perror(err);
       exit(1);
  }

typedef struct {

  char transMode ;
  uint8_t linkAddr;

} ChanAccessReqParam;


typedef struct {
HeaderInt H;
ChanAccessReqParam P;

} ChanAccesReq ;
  int main(int argc, char *argv[])
  {
       int sock;                        /* Socket descriptor */
       struct sockaddr_in echoServAddr; /* Echo server address */
       struct sockaddr_in fromAddr;     /* Source address of echo */
       unsigned short echoServPort;     /* Echo server port */
       unsigned int fromSize;           /* In-out of address size for recvfrom() */
       char *servIP;                    /* IP address of server */
       int structLen;                   /* Length of string to echo */
       int respStringLen;               /* Length of received response */


       if (!argv[1]) {
              fprintf(stderr,"No server IP sepcified at arg 1\n");
              exit(1);
       }

       else if (!argv[2]) {
              fprintf(stderr,"No port Number Sepcified at arg 2\n");
              exit(2);
       }




        ChanAccesReq test { 1 ,1,0,'c',15};
        

        servIP = argv[1];           /* First arg: server IP address (dotted quad) */
        echoServPort = atoi(argv[2]);  /* Use given port, if any */

        /* Create a datagram/UDP socket */
        if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
               DieWithError("socket() failed");

        /* Construct the server address structure */
        memset(&echoServAddr, 0, sizeof(echoServAddr));    /* Zero out structure */
        echoServAddr.sin_family = AF_INET;                 /* Internet addr family */
        echoServAddr.sin_addr.s_addr = inet_addr(servIP);  /* Server IP address */
        echoServAddr.sin_port   = htons(echoServPort);     /* Server port */

        int tempint = 0;


        tempint = sendto(sock, (ChanAccesInd*)&test, 10+(sizeof(test)), 0, (struct sockaddr *)
         &echoServAddr, sizeof(echoServAddr));

        if (tempint == -1 ) {

               printf("Sent struct size: %d\n", tempint);
               DieWithError("sendto() sent a different number of bytes than expected\n");
        }

        close(sock);
        exit(0);
}

感谢您的帮助

【问题讨论】:

  • 无关说明:这不是 C++ 代码。它是带有偶尔 IO 流的 C 代码。
  • 同意 xd 我更新
  • 建议:对recvfromread 返回的值多加一点吸引力。是的,负数是彻底失败,但零和正数包含重要信息,例如从套接字读取的字节数。 UDP 比 TCP 更宽容,您会收到一个且只有一个完整的消息,但该消息可能仍然不是您所期望的。
  • @user4581301 谢谢你的帮助,我会改变的,你知道如何帮助我吗?我知道它有两种不同的类型,但我确定有一种方法我不必发送两次消息,因为每次发送时所有信息都是相同的
  • 在快速阅读时没有什么让我跳出来的,我现在正在摇晃 Windows 机器,所以我无法通过调试器编译和清洗程序。说到调试器,您应该能够使用工具附带的任何调试器来浏览服务器,看看它到底做了什么。最好的情况是你意识到问题并解决它。最坏的情况是您有更多信息要添加到问题中。

标签: c sockets udp sendto recvfrom


【解决方案1】:

您可以在 recvfrom 上使用标志,例如“PEEK”,请在此处查看修改后的代码:

 recvfrom(sock, HeaderStruct, sizeof(*HeaderStruct), MSG_PEEK,(struct sockaddr *) &echoClntAddr, &cliAddrLen);

这里有完整的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "struct.h"
#include <iostream>


void GesCanSrvRecvMsg(){

     int sock;                        // Socket
     struct sockaddr_in echoServAddr; // Adresse local
     struct sockaddr_in echoClntAddr; // Adresse Client
     unsigned int cliAddrLen;         // Longeur des messages entrant
     unsigned short echoServPort;     // Port Serveur
     unsigned recvMsgSize;                 // Taille message reçu

    //Creation allocation memoire necessaire pour recevoir les structures
   HeaderInt *HeaderStruct=(HeaderInt *)malloc(sizeof(HeaderInt));
   ChanAccesReq *ChanReqStruct=(ChanAccesReq *)malloc(sizeof(ChanReqStruct));
   TransTimeReq *TransTReqStruct=(TransTimeReq *)malloc(sizeof(TransTReqStruct));
   TransCancelReq *TransCReqStruct=(TransCancelReq *)malloc(sizeof(TransCReqStruct));
    //Port definie a 13000

   echoServPort = atoi("13000");  //Premier argument port local
  //Creation socket
  if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
    Error("socket() failed");

  //Construct local address structure
  memset(&echoServAddr, 0, sizeof(echoServAddr));
  echoServAddr.sin_family = AF_INET;
  echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  echoServAddr.sin_port = htons(echoServPort);

  //Bind a ladresse
  if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
     Error("bind() failed");

  while(1) //Boucle infinie
  {

         cliAddrLen = sizeof(echoClntAddr);
         HeaderStruct->code=0;

         /*Attente des messages en ne regarde que l'entete Header
         Flag -> MSG_PEEK pour eviter un double envoie du client*/
         recvfrom(sock, HeaderStruct, sizeof(*HeaderStruct), MSG_PEEK,(struct sockaddr *) &echoClntAddr, &cliAddrLen);
         std::cout<<"coucou"<<'\n';
         char* subscIPAddr=inet_ntoa(echoClntAddr.sin_addr); // adresse iP Client



         if (unsigned(HeaderStruct->code)==ChanAccessReqc){ //Si code == ChanAccessReqC (1)
            recv(sock,ChanReqStruct,sizeof(*ChanReqStruct),0);
            CSMAChnAccessLstReq.push_back(*ChanReqStruct); //Placement dans liste partagée avec GesCanSlot


        }

        else if (unsigned(HeaderStruct->code==TransTimeReqc)){ //Si code == TransTimeReqc (3)
            recv(sock,TransTReqStruct,sizeof(*TransTReqStruct),0);
            CSMATimeTransmLstReq.push_back(*TransTReqStruct);

        }
        else if (unsigned(HeaderStruct->code==TransCancelReqc)){ //Si code == TransCancelReqc (3)
            recv(sock,TransCReqStruct,sizeof(*TransCReqStruct),0);
            TransacCancelLst.push_back(*TransCReqStruct);
        }

        else {
            Error("Error");
        }
   }
   close(sock);
};

int main(int argc, char *argv[])
{


        GesCanSrvRecvMsg();


        return 0;
  }

【讨论】:

  • 请注意,下一个readrecv 可能会读取不同的数据报。 UDP 数据报总是可以丢弃的,并且实现可以在任何时候随意丢弃它们,因此您偷看的消息可能会在您有机会readrecv 之前被丢弃。然后您可能会readrecv 一个后续数据报。
  • 感谢@DavidSchwartz 提供详细信息,但由于它是同一个套接字,而且我们只是对同一条消息进行第二次阅读,我们应该没有任何问题吗?他只是使用不同的结构(据我了解)来分析他的缓冲区?
  • 为什么你认为他不会有任何问题?是因为你想不出任何可能发生的方式吗?您无法从无法想出可能出错的方式来创建保证。你要么有保证,要么没有。并且不能保证 UDP 数据报不会被丢弃。我能想到这种情况发生的方式,并且已经看到它发生,但那是因为我已经编程了几十年。避免需要深厚的专业知识和经验的方法是仅依靠您实际拥有的保证,并了解当他们说数据报可丢弃时,这是事实。
猜你喜欢
  • 1970-01-01
  • 2012-07-27
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
  • 2022-01-15
相关资源
最近更新 更多