无法传输二进制文件而文本文件可以传输

时间:2013-04-16 13:34:42

标签: c sockets network-programming file-transfer

我需要将文件从一个应用程序转移到同一台机器上的另一个应用程序。我编写了一个代码来传输一个包含以下字段的数据包结构:

  • seq no
  • 数据,这是一个unsigned char,表示要传输的数据
  • 长度,表示使用read函数读取了多少字节
  • crc ,这是现在的默认字符串

和一个参数last,表示它是最后一个数据包,以便可以关闭文件和连接。

此代码适用于ASCII文件,但是,当我使用它来传输二进制文件时,两个文件的差异不匹配。我无法弄清问题是什么。有人可以帮助我吗?

以下是我的发件人的代码。它连接到端口4950上的接收器。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define DATA 170
#define SERVERPORT "4950"       // the port users will be connecting to

typedef struct packet_ {
    unsigned short int seq;
    unsigned short int round;
    unsigned short int last;
    unsigned short int length;
    unsigned char  data[DATA];
    unsigned char crc[17];
} packet;

int main(int argc, char *argv[])
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;
    int max_seq_no = 10;
    packet send;
    FILE *fp;
    unsigned short int seq =0, round =0;
    long int totalBytes = 0;
    unsigned short int bytes_read =0;

    if (argc != 3) {
        fprintf(stderr,"usage: talker hostname message\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE; // use my IP
    if ((rv = getaddrinfo(NULL, SERVERPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and make a socket
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                             p->ai_protocol)) == -1) {
            perror("talker: socket");
            continue;
        }
        break;
    }

    if (p == NULL) {
        fprintf(stderr, "talker: failed to bind socket\n");
        return 2;
    }

    fp = fopen("wilde-tiobe.txt", "rb");
    printf("Size of packet : %d", sizeof send);

    while (1) {
        memset((void *) &send, '\0', sizeof send);

        send.seq = seq;
        send.round = round;
        memset(send.data, '\0', DATA);
        bytes_read = read(fileno(fp), send.data, DATA );
        send.length = bytes_read;
        if (bytes_read < DATA) {
            send.last = 1;
            printf("****last byte***\n");
        }
        strcpy(send.crc, "yet to calcula\0");
        totalBytes+= bytes_read;

        //if(bytes_read == 0) break;

        if ((numbytes = sendto(sockfd, &send, sizeof send, 0,
                               p->ai_addr, p->ai_addrlen)) == -1) {
            perror("talker: sendto");
            exit(1);
        }
        usleep(60000);
        if (send.last) break;
    }

    fclose(fp);
    printf("Total bytes transferred : %lu\n", totalBytes);

    freeaddrinfo(servinfo);

    close(sockfd);

    return 0;
}

这是接收器的代码:

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

#define MYPORT "4950"   // the port users will be connecting to

#define MAXBUFLEN 196
#define DATA 170

typedef struct packet_ {
    unsigned short int seq;
    unsigned short int round;
    unsigned short int last;
    unsigned short int length;
    unsigned char data[DATA];
    unsigned char crc[17];
} packet;

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    FILE *fp;
    int numbytes;
    struct sockaddr_storage their_addr;
    char buf[MAXBUFLEN];
    socklen_t addr_len;
    char s[INET6_ADDRSTRLEN];
    packet * recv, recv_packet;
    fp = fopen("wtf.bmp", "wb");

    printf("size of packet :%d", sizeof(packet));
    memset(&hints, 0, sizeof hints);

    hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                             p->ai_protocol)) == -1) {
            perror("listener: socket");
            continue;
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("listener: bind");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "listener: failed to bind socket\n");
        return 2;
    }

    freeaddrinfo(servinfo);

    addr_len = sizeof their_addr;

    while(1) {
        printf("listener: waiting to recvfrom...\n");
        if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN , 0,
                                 (struct sockaddr *)&their_addr,
                                  &addr_len)) == -1) {
            perror("recvfrom");
            exit(1);
        }
        printf("listener: got packet from %s\n",
                inet_ntop(their_addr.ss_family,
                        get_in_addr((struct sockaddr *)&their_addr),
                        s, sizeof s));
        printf("listener: packet is %d bytes long\n", numbytes);
        //printf("listener: packet contains \"%s\"\n", buf);
        recv = (packet *) buf;
        if (!recv_packet.data) {
            printf("NO memory\n");
            break;
        }
        memset(&recv_packet, 0, sizeof recv_packet);
        recv_packet.seq  = recv->seq;
        recv_packet.round = recv->round;
        recv_packet.last = recv->last;
        recv_packet.length = recv->length;
        memset(recv_packet.data, '\0', DATA);
        strncpy(recv_packet.data, recv->data, recv_packet.length);
        strncpy(recv_packet.crc, recv->crc, 17);
        //printf(" len: %hu  and data: %s \n", recv_packet.length, recv_packet.data);
        write(fileno(fp), recv_packet.data, recv_packet.length);
        if (recv_packet.last) {
            printf("Last packet\n");
            break;
        }
    }

    fclose(fp);
    close(sockfd);

    return 0;
}

1 个答案:

答案 0 :(得分:1)

我已经遇到类似socket的问题,因为我发送的是“unsigned char []”的缓冲区,并且接收到“char []”的缓冲区,我不认为是你的问题因为你没有做任何操作在签名数组中,但任何方式都可以避免这些演员阵容,值得一试。

使用

unsigned char buf[MAXBUFLEN];

而不是

char buf[MAXBUFLEN];

在接收数据包之前还清除缓冲区,并在继续进程之前检查“numbytes”的值。 例如:

    printf("listener: waiting to recvfrom...\n");
    memset(buff, 0, sizeof(buff));
    numbytes=0;
    while ( (numbytes += recvfrom(sockfd, &buf[numbytes], MAXBUFLEN - numbytes , 0,
                                 (struct sockaddr *)&their_addr,
                              &addr_len)) < sizeof(packet))
        printf("Oops, the package is not full, lets read it again.");
    }

或者在使用recvfrom读取整个内存块时尝试传递MSG_WAITALL标志,但我仍然更愿意检查每次读取的数据量。