UDP套接字:服务器将文件发送到客户端协议族不支持的地址族

时间:2016-03-11 14:54:07

标签: c sockets udp

我只是套接字编程的初学者,目前正致力于使用UDP处理文件传输的小程序。该程序用C语言编写。

这是我的问题:
UDP服务器将首先使用recvfrom()函数从UDP客户端捕获消息,以便开始发送文件。我首先发送了文件名,但它无法通过,出现在:协议族不支持的地址族作为错误消息()。我检查了client_addr的sin_family,它是7.此外,在我尝试设置 client_addr.sin_family = AF_INET 之后;服务器工作正常,但客户端甚至无法收到任何消息。

我检查了一些消息来源,但碰巧没有那么有用,如果有人知道为什么并愿意告诉我。感谢您的所有帮助。

以下是服务器代码的一小部分:

int socketfd;
/* my address information */
struct sockaddr_in server_addr;
/* connector’s address information */
struct sockaddr_in client_addr;
socklen_t clientLength;
int numbytes;
char buffer[BUFLEN];
int portNum = atoi(port);
time_t timer;
char charfileSize[20];
int percent, count = 0;
struct tm *tm_info;
char timeBuf[30];
if((socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
  perror("Server-socket() sockfd error lol!");
  exit(1);
} else {
  printf("Server-socket() sockfd is OK...\n");
}

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNum);
server_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(server_addr.sin_zero), 0, 8);

// bind the socket to the server ip address
if(bind(socketfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
  perror("server socket bind to IP error lol!\n");
  exit(1);
} else {
  printf("successfully bind server ip with socket ...\n");
}
// client_addr.sin_port = htons(portNum);
client_addr.sin_family = AF_INET;

//*  for ensuring client connection  *//
int tempGet;
char tempBuf[BUFLEN];
//if( (tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength)) > 0 ) {
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);
if( strcmp( tempBuf, "send file" ) == 0) {
  printf("Can start transferring file...\n");
}
printf("sin family:%d\n", client_addr.sin_family);

FILE *fp = fopen(filename, "rb");
if ( !fp ) {
  perror("Error opening the file\n");
  exit(1);
} else {
  // successfully opened the file that's going to be transferred
  printf("file opened: %s\n", filename);

  // get file size
  int file_block_size = 0;
  bzero(buffer, BUFLEN);
  long file_size;
  fseek(fp, 0, SEEK_END);
  file_size = ftell(fp);
  // goes back to the beginning of file(fp)
  rewind(fp);
  printf("file name: %s\n", filename);
  printf("file size: %ld kb\n", file_size/1024);
  // get time
  time(&timer);
  clientLength = sizeof(client_addr);

  //client_addr.sin_family = AF_INET;
  int sendFileName;
  // length of file name
  if( (sendFileName = sendto(socketfd, filename, strlen(filename), 0, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in))) >= 0) {
    printf("file name sent.\n");
  } else {
    //printf("%d\n", sendFileName);
    perror("file name send error.\n");
    exit(1);
  }
}

2 个答案:

答案 0 :(得分:2)

recvfrom的最后一个参数采用socklen_t的地址,需要使用sockaddr参数的大小进行初始化。在此调用之前,我没有看到clientLength已初始化,因此当此函数返回时,client_addr可能无法正确更新。这会导致对sendto的后续调用失败。

如果您在致电clientLength之前初始化recvfrom,那将会解决问题。

clientLength = sizeof(client_addr);
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);

答案 1 :(得分:0)

我对Stack Overflow上所有错误和半正确的UDP套接字程序尝试感到有些厌倦。我已经决定进行参考UDP实现,以此问题为例。下次我看到TCP问题(并且有一些时间和精力),我将参考TCP实现。

这个实现正确地完成了所有事情(据我所知:)。它使用BSD套接字,因此Windows实现必须进行微小的更改。除此之外,它应该是C-99所有。

错误处理:应用程序检查可能的错误,但不会尝试纠正错误,只需报告。还知道在某些错误条件下泄漏套接字(导致应用程序关闭),但在这种情况下添加正确的套接字将只需要更多的代码而不会实际增加很多好处。

编译:

gcc -std=c99 -g -D_POSIX_SOURCE -Wall -Werror -pedantic -o test_udp send_udp.c

用法: ./ test_udp服务器 ./ test_udp< hostname> <文件名>

看哪:我给你 test_udp.c

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

const short port = 4321;

int run_server();
int send_file(const char* const server, const char* const file);

int main(int argc, char* argv[]) {
    if (argc < 2 || argc > 3) {
        printf("Error: Usage send_udp (server|<server_host>) [<file name>]\n");
        return 1;
    }

    if (!strcmp(argv[1], "server"))
        return run_server(); // runs forever

    if (argc != 3) {
        printf("Error: client mode accepts two arguments: server and file name.\n");
        return 1;
    }

    return send_file(argv[1], argv[2]);
}

int run_server() {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        printf("run_server(): error creating socket: %s\n", strerror(errno));
        return 2;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {
        printf("run_server(): error binding socket: %s\n", strerror(errno));
        return 2;
    }
    printf("run_server(): socket created and bound, entering endless loop.\n");

    struct sockaddr_in incoming;
    socklen_t incoming_sz = sizeof(incoming);
    char buff[USHRT_MAX]; // maximum size of UDP datagramm
    const char* buff_ptr = buff;
    while (1) {
        ssize_t msg_size = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&incoming), &incoming_sz);
        if (msg_size == -1) {
            printf("run_server(): error receiving message: %s\n", strerror(errno));
            return 3;
        }

        if (msg_size == 0) {
            printf("run_server(): a message of 0 size received from the client, bogus. Skipping, continue to listen.\n");
            continue;
        }

        // Message structure: unsigned 16 bit length of file name (network order), followed by filename, followed by data
        uint16_t fname_len;
        if (msg_size < sizeof(fname_len)) {
            printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
            continue;
        }

        memcpy(&fname_len, buff_ptr, sizeof(fname_len));
        fname_len = ntohs(fname_len);
        buff_ptr += sizeof(fname_len);
        msg_size -= sizeof(fname_len);
        if (msg_size < fname_len) {
            printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
            continue;
        }

        char file_name[fname_len + 1];
        strncpy(file_name, buff_ptr, fname_len);
        file_name[fname_len] = '\0';
        buff_ptr += fname_len;
        msg_size -= fname_len;
        printf("run_server(): incoming transfer for file %s, intrinsic file size: %zu\n", file_name, msg_size);

        FILE* f = fopen(file_name, "wb");
        if (!f) {
            printf("run_server(): Could not open file for writing. Skipping the message, carrying on.\n");
            continue;
        }

        size_t written = fwrite(buff_ptr, msg_size, 1, f);
        if (written != 1)
            printf("run_server(): error, could not write whole file.\n");
        else
            printf("run_server(): incoming file written successfully.\n");

        fclose(f);
    }

    return 0;
}

int send_file(const char* const server, const char* const file) {
    uint16_t fname_len = strlen(file);
    uint16_t max_short = 0;
    max_short = ~max_short;
    if (fname_len > (max_short - sizeof(fname_len))) {
        printf("send_file(): file name is toooo large. Can't send this file.\n");
        return 2;
    }
    FILE* f = fopen(file, "rb");
    if (!f) {
        printf("send_file(): Could not open file for reading. Nothing sent.\n");
        return 3;
    }
    fseek(f, 0, SEEK_END);
    unsigned long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);
    if (fsize > max_short - sizeof(fname_len) - fname_len) {
        printf("send_file(): file is toooo large. Can't send this file.\n");
        fclose(f);
        return 2;
    }

    char buff[sizeof(fname_len) + fname_len + fsize];
    char* buff_ptr = buff;
    uint16_t net_fname_len = htons(fname_len);
    memcpy(buff_ptr, &net_fname_len, sizeof(net_fname_len));
    buff_ptr += sizeof(net_fname_len);
    memcpy(buff_ptr, file, fname_len);
    buff_ptr += fname_len;

    size_t rc = fread(buff_ptr, fsize, 1, f);
    if (rc != 1) {
        printf("send_file(): Could not read whole file. Error.\n");
        fclose(f);
        return 3;
    }
    fclose(f);

    struct addrinfo* ainfo;
    if (getaddrinfo(server, NULL, NULL, &ainfo) != 0) {
        printf("send_file(): Unknown host %s.\n", server);
        return 3;
    }
    struct addrinfo* ainfo_begin = ainfo;

    // I will take the first IP v4 entry in possible addressess
    while (ainfo->ai_family != AF_INET && ainfo->ai_next)
        ainfo = ainfo->ai_next;

    if (ainfo->ai_family != AF_INET) {
        printf("send_file(): Couldn't resolve host %s to AF_INET address.\n", server);
        return 3;
    }

    int addr = ((struct sockaddr_in*)(ainfo->ai_addr))->sin_addr.s_addr;
    freeaddrinfo(ainfo_begin);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        printf("read_file(): error creating socket: %s\n", strerror(errno));
        return 2;
    }

    struct sockaddr_in remote_addr;
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_port = htons(port);
    remote_addr.sin_addr.s_addr = addr;

    ssize_t sent = sendto(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&remote_addr), sizeof(remote_addr));
    if (sent != sizeof(buff))
        printf("read_file(): error sending message over socket: %s, only %zu bytes sent.\n", strerror(errno), sent);
    else
        printf("read_file(): file sent successfully.\n");

    return sent == sizeof(buff) ? 0 : 3;
}