【问题标题】:Problem with simple Client/Server, file gets printed wrong简单客户端/服务器出现问题,文件打印错误
【发布时间】:2019-06-26 06:48:16
【问题描述】:

基本上,服务器需要将客户端填充的结构打印到文件中,当 message.fine == 1 时,客户端和服务器都应该停止。问题是每次创建文件时,都会填充数千为零,表示读写功能没有正常工作。你能帮我解决这个问题吗?

这是客户:

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

struct Data 
{
    int a;
    float b;
    int fine;
    int risultato;
};

int main()
{   
    int sock;
    struct Data message, response;
    message.fine = 0;

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(6869);
    address.sin_addr.s_addr = inet_addr("127.0.0.1");

    sock = socket(AF_INET, SOCK_STREAM, 0);

    connect(sock, (struct sockaddr *)&address, sizeof(address));

    while(message.fine != 1)
    {   
        printf("\nInsert an integer number:");
        scanf("%d", &message.a);
        printf("\nInsert a float number:");
        scanf("%f", &message.b);
        printf("\nIs this the final message? (0 = no, 1 = yes)\n");
        scanf("%d", &message.fine);


        write(sock, &message, sizeof(struct Data));
        read(sock, &response, sizeof(struct Data));

        if(response.risultato == -1)
        {
            fprintf(stderr, "\nServer returned Writing Error\n");
            exit(EXIT_FAILURE);
        }
        else if(response.risultato == 1)
        {
            printf("\nSuccess in Operation\n");
        }
    }
    close(sock);
    exit(EXIT_SUCCESS);
}

这是服务器:

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

struct Data 
{
    int a;
    float b;
    int fine;
    int risultato;
};



void * handler(void * args)
{
    struct Data message;    
    int sock = *(int *)args;
    FILE *fp;

     if(!(fp = fopen("result.txt", "a")))
        {
        fprintf(stderr, "Error in opening file\n");
        exit(EXIT_FAILURE);
        }

    while(1)
    {

        read(sock, &message, sizeof(struct Data));

        printf("%d, %f, %d", message.a, message.b, message.fine);

        if(fprintf(fp,"\n[%d] [%.2f]\n", message.a, message.b) < 0)
        {
            message.risultato = -1;
        }
        else{
            message.risultato = 1;
        }

        if(message.fine == 1)
        {
            break;
        }
        write(sock, &message, sizeof(struct Data));

    }
    fclose(fp);
    close(sock);
    free(args);
    pthread_exit(NULL);
}


int main()
{
    int sock, client_sock;
    int * new_sock;

    pthread_t tid;

    struct sockaddr_in address, client_address;
    address.sin_family = AF_INET;
    address.sin_port = htons(6869);
    address.sin_addr.s_addr = inet_addr("127.0.0.1");

    socklen_t sock_len = (socklen_t)sizeof(address);


    sock = socket(AF_INET, SOCK_STREAM, 0);

    bind(sock, (struct sockaddr *)&address, sock_len);

    listen(sock, 3);

    while(1)
    {
       new_sock = (int *)malloc(sizeof(int));
       sock_len = sizeof(client_address);
       client_sock = accept(sock, (struct sockaddr *)&client_address, &sock_len); 
       *new_sock = client_sock;       
       pthread_create(&tid, NULL, handler, (void *)new_sock);
       pthread_detach(tid);
    }

}

实际结果应该是文件上依次打印的message.a和message.b中的数字。

【问题讨论】:

  • 为什么每次迭代都分配一个new_sock
  • 为什么不检查readwrite的返回值?
  • 我会检查读写的返回值,但我认为不会解决问题...
  • read() 不保证它会返回 sizeof(struct Data) 字节。因此,您必须修改代码以根据需要执行尽可能多的read(),直到您积累了一条消息。
  • 您不知道read()write() 有多少字节。这些调用返回一个值是有原因的。 TCP 连接是一个流,您可以读取/写入 任何 个字节数。你“认为这不能解决问题”,但是你是那个问你问题的人,因为你无法解决它。

标签: c sockets pthreads


【解决方案1】:

问题是每次创建文件都会被填上千个零,说明读写功能没有正常工作

警告您使用 SOCK_STREAM,您无法保证在 一个 读取时读取所有预期的字节。

在服务器中你需要检查结果

   read(sock, &message, sizeof(struct Data));

如果未读取所有内容,则中断错误或再次读取,否则在未读取任何内容或读取的内容小于预期大小时 消息 完全或部分未更改或未设置,如果它位于第一个循环,您有一个 机会 循环到时间结束,当然,您在文件中打印的值也未更改或无效。

您可能遇到的情况是:在服务器中的交换期间(可能是第一次)read 没有读取所有结构,因为 message.fine 未设置为 1并且一些其他值也未设置,因为 message.fine != 1 你再次循环并再次读取,但从未读字节而不是从结构的开头读取,所以即使你读取 sizeof(Data) 字节,当时结构没有很好地填充,再次message.fine 未设置为 1,其他属性未设置正确的值等,此时没有什么可读取的,您无限循环,因为 message 保持不变。

当然我鼓励你也检查一下的结果,你在客户端读的时候也遇到同样的问题

【讨论】:

  • 提建议,但我认为这不能解决问题
  • 好的,我会试试看它是否有效,顺便说一句,我仍然愿意接受其他解决方案
  • @St3veR0nix 我是在 stream 上阅读的,您不会像 UDP 那样阅读固定大小的块。我编辑了我的答案
【解决方案2】:

必须修改您的客户端/服务器端读取代码以检查读取调用的返回值。请记住,当您使用 SOCK_STREAM 时,发送和接收的所有数据都作为流(而不是数据报)发送和接收,导致后续发送的数据被放在一起,同样不需要接收单个发送的所有数据一个去另一边。

服务器修改代码sn-p(只是伪代码),请检查并适当修改您的原始代码:

while(1)
{
    int res_bytes = 0;
    int res = 0;
    while (res_bytes < sizeof(struct Data))
    {
        res = read(sock, &message+res_bytes, (sizeof(struct Data) - res_bytes));
        if ((res == 0) || (res == -1))
        {
            printf("read returned an error, returning");
            return;
        }
        res_bytes = res_bytes + res;
    }

    printf("%d, %f, %d", message.a, message.b, message.fine);

    if(fprintf(fp,"\n[%d] [%.2f]\n", message.a, message.b) < 0)
    {
        message.risultato = -1;
    }
    else{
        message.risultato = 1;
    }

    if(message.fine == 1)
    {
        break;
    }
    res = write(sock, &message, sizeof(struct Data));
    if ((res == 0) || (res == -1))
    {
       printf("Error while writing");
       return;
    }
}

【讨论】:

    【解决方案3】:

    感谢@bruno 和@Jay 的帮助和所有建议。我这样安排阅读部分:

    do{
    
       res = read(sock, &message, sizeof(struct Data));
    
      }while(res < sizeof(struct Data));
    

    同样的写法,现在可以了!

    附:我注意到,如果我在 fprintf 成功后不刷新文件流,则文件不会被打印,它会在循环中断后打印它。为什么会这样?

    【讨论】:

    • 我不确定这是否适用于所有情况。例如:如果我们认为 int 和 float 分别为 4 个字节,那么 sizeof(struct Data) 将为 16。但是,假设您在第一次读取调用时收到 4 个字节,那么您需要通过 read(sock, &message+4 , (16-4) 在第二次调用中,否则您将覆盖先前读取的数据。
    猜你喜欢
    • 1970-01-01
    • 2018-03-22
    • 2018-01-30
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 2015-06-19
    • 2021-10-12
    • 1970-01-01
    相关资源
    最近更新 更多