【问题标题】:Not able to receive or send entire packet in socket programming using C无法在使用 C 的套接字编程中接收或发送整个数据包
【发布时间】:2015-02-03 21:28:09
【问题描述】:

我一直在尝试通过套接字将数据包从客户端发送到服务器。在一些技巧的帮助下,我的代码取得了相当大的进步。但是,服务器只从客户端接收八个字节并将它们打印在控制台上,而在我的客户端,它似乎已经发送了所有内容。

现在我不确定问题出在发送方还是接收方。我的直觉是我的客户端出了点问题。有人可以帮忙验证我的假设吗?

客户端代码:

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    data_struct client_data;
    struct packet
    { 
        long int srcID;
        long int destID;
        int pver;
        int profiles;
        int length;
        long int data;
    };


    if (argc < 3) {
        fprintf(stderr,"usage: %s hostname port\n", argv[0]);
        exit(0);
    }
    portno = atoi(argv[2]); //Convert ASCII to integer
    sockfd = socket(AF_INET, SOCK_STREAM, 0); // socket file descriptor

    if (sockfd < 0) 
        error("ERROR DETECTED !!! Problem in opening socket\n");

    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR DETECTED !!!, no such server found \n");
        exit(0);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr)); //clear the memory for server address

    serv_addr.sin_family = AF_INET;    
    bcopy((char *)server->h_addr, 
        (char *)&serv_addr.sin_addr.s_addr,
        server->h_length);

    serv_addr.sin_port = htons(portno);

    printf("Client 1 trying to connect with server host %s on port %d\n", argv[1], portno); 


    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR in connection");

    printf("SUCCESS !!! Connection established \n");

    char buffer[256];
    struct packet *pkt = (struct packet *) buffer;
    char *payload = buffer + sizeof(struct packet);
    long double packet_size;



    printf("Started Creating packet\n");
    pkt->srcID = 01;
    pkt->destID = 02;
    pkt->pver = 03;
    pkt->profiles = 01;
    pkt->length = 16;
    pkt->data = 1; 2; 3; 4; 5; 6; 7; 8;
    {
        if (send(sockfd,pkt,sizeof(packet_size),0) <0)
            printf ("error\n");
        else
            printf ("packet send done");
    }

    return 0;
}

服务器代码:

int main(int argc, char *argv[])
{
    int sockfd, newsockfd, portno, clilen;
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    char wish;

    long int SrcID;
    long int DestID;
    int Pver;
    int Profiles;
    long int Data;
    int Length;
    char bytes_to_receive;
    int received_bytes;
    struct packet
    { 
        long int srcID;
        long int destID;
        int pver;
        int profiles;
        int length;
        long int data;
    };

    if (argc < 2) {
        fprintf(stderr,"usage: %s port_number1",argv[0]);
        exit(1);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR DETECTED !!! Problem in opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = atoi(argv[1]);

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(portno);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
        error("ERROR DETECTED !!! There was a problem in binding");

    listen(sockfd, 10);
    clilen = sizeof(cli_addr);
    printf("Server listening on port number %d...\n", serv_addr.sin_port); 
    newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);

    if (newsockfd < 0) 
        error("ERROR DETECTED !!! the connection request was not accepted");

    char buffer[256];
    struct packet *pkt = (struct packet *) buffer;
    char *payload = buffer + sizeof(struct packet);
    long double packet_size;

    bytes_to_receive = sizeof(packet_size);
    received_bytes = 0;
    int rc =0;


    while ((rc = recv(newsockfd,pkt,sizeof(packet_size),0)) > 0)
    {
        received_bytes+=rc;
        SrcID = pkt->srcID;
        DestID = pkt->destID;
        Pver = pkt->pver ;
        Profiles = pkt->profiles;
        Length = pkt->length;
        Data = pkt->data;
        printf("Data Received from Client_1 are :\n");
        printf("Source ID: %ld\n", SrcID);
        printf("Destination ID: %ld\n", DestID);
        printf("profile Version: %d\n", Pver);
        printf("No of Profiles: %d\n", Profiles);
        printf("Length: %d\n", Length);
        printf("data : %ld\n", Data);
    }
    if (rc == 0)
    {
        printf("Connection closed by Server\n");
        printf("Bytes received: %d\n",received_bytes);
    }

    if (rc == -1)
    {
        perror("recv");
    }
    {
        if (close(newsockfd) == -1) {
            error("Error closing connection with client 1");
        }

        printf("Connection with client 1 has been closed\n");
    }
    return 0; 

}

我在客户端控制台上看到的输出是:

Client Side:  Client 1 trying to connect with server host 130.191.166.230 on port 1234
SUCCESS !!! Connection established 
Started Creating packet
packet send done

在服务器的控制台上我看到了:

Server Side:  Data Received from Client_1 are :
Source ID: 1
Destination ID: 2
profile Version: 0
No of Profiles: 1074462536
Length: 0
data : 0
Connection closed by Server
Bytes received: 8
Connection with client 1 has been closed

【问题讨论】:

  • 请看一下代码是如何格式化的——缩进完全不可读。
  • 我昨天就这样回答了一个:stackoverflow.com/questions/6791022
  • 因为您发送的字节数是 8 个字节。这行send(sockfd,pkt,sizeof(packet_size),0) 等价于send(sockfd,pkt, 8, 0);
  • 这行pkt-&gt;data = 1; 2; 3; 4; 5; 6; 7; 8;是什么意思
  • 但我将 packet_size 定义为 long double...16 字节对吗??

标签: c++ c sockets networking network-programming


【解决方案1】:

首先

recv(newsockfd,pkt,sizeof(packet_size),0)) /* What is packet_size ? */
recv(newsockfd,pkt,sizeof(struct packet),0)) /* You probably mean this. */

这可能会解决您的问题,但您使用 TCP 套接字的方式存在一些问题。

但在我的客户端,它会打印出所有内容都已发送

在哪里?我没有看到您实际检查发送的字节数。 send(2) 可以在发送少于您要求的数量后返回。

它显示客户端只发送了 8 个字节并将它们打印出来。

TCP 是一种面向流的协议。您发送字节并且它们以相同的顺序到达。因此,当您 recv(2) 某事时,您可能会得到更少(或比您写的更多)。因此,以下可能是正确的:

client:
send 100 bytes
send 400 bytes

server:
recv 50 bytes
recv 150 bytes
recv 250 bytes
recv 50 bytes

使用TCP 时,sendrecv 的调用次数不必相同。

【讨论】:

  • 所以你的意思是说,客户端可能正在发送所有字节但我的recv只接收了几个,所以我怎么能保证发送和接收是正确的......
  • 刷新页面看看我的回答(packet_size是什么?)。您可以继续接收,直到获得完整的packet
  • packet_size 定义为“long double”。
  • @user537670 struct packet 比那个大
  • @user537670:那是给回答者的。看起来你有很多问题,你应该阅读 Steven 的网络圣经(TCP Illustrated vol1,UNIX 网络编程)。
【解决方案2】:

当您调用send 时,该函数会返回实际发送的字节数,此数字可能小于您要发送的字节数。所以每次你想发送东西时,都必须有一个像下面这样的循环

bool sendBuffer(SOCKET s, unsigned char *buf, int size)
{
    while (size > 0)
    {
        int sz = send(s, buf, size,0);
        if (sz < 0) return false; // Failure
        size -= sz; // Decrement number of bytes to send
        buf += sz;  // Advance read pointer
    }
    return true; // All buffer has been sent
}

接收时必须执行类似的循环(换句话说,recv 可以返回比您要求的字节少的字节)。

如果您不进行这些循环,则风险在于,当您在本地计算机甚至 LAN 上工作时,显然一切都会正常工作(直到以太网数据包的大小),但是在互联网上工作时,事情将无法正常工作。

另请注意,正如其他答案指出的那样,您要求发送sizeof(packet_size),即存储该变量所需的字节数,而不是结构的大小。

【讨论】:

  • 你确定你的函数应该返回bool*吗?
  • @6502:谢谢,在我的数据包的数据字段中还有一件事我想发送数字 1、2、3、4、5、6,......这可以是数字的随机和最大限制为 500。这里显示它刚刚收到数字“1”。如何做到这一点?是不是因为我不想做你建议的循环。
【解决方案3】:

有一条非正式的规则,任何人都不得编写任何使用 TCP 的软件,直到他们记住这句话并能完全解释其含义:“TCP 是一种不保留应用程序消息边界的字节流协议。”

这意味着 TCP 仅确保您以相同的顺序输出您输入的相同字节。它不会以任何方式将字节“粘合”在一起。

在编写任何使用 TCP 的代码之前,您应该使用已经设计好的协议(例如 IMAP 或 HTTP)或者自己设计一个。如果你自己设计一个,你应该写出一个协议规范。它应该明确定义协议级消息在字节级将包含什么。它应该具体说明接收者如何找到消息的结尾,等等。

这对于简单的应用程序来说可能看起来有点傻,但相信我,它会带来巨大的回报。否则,几乎不可能弄清楚为什么事情不正常,因为如果服务器和客户端不能很好地相处,就没有仲裁者来判断什么是正确的。

【讨论】:

    【解决方案4】:

    我不擅长套接字编程,但我注意到了一些事情。据我所知,我认为您不能那么容易地通过套接字发送结构。您不妨考虑另一种方法。

    注意,使用 send/recv 时,您还要确定 packet_size 的大小,而不是结构的大小。

    谷歌搜索提出了关于通过套接字发送结构的问题:http://ubuntuforums.org/showthread.php?t=613906

    【讨论】:

      猜你喜欢
      • 2020-02-29
      • 1970-01-01
      • 1970-01-01
      • 2013-07-04
      • 1970-01-01
      • 1970-01-01
      • 2015-11-26
      • 2010-11-13
      • 1970-01-01
      相关资源
      最近更新 更多