【问题标题】:UDP server/client in C - sendto error: Address family not supported by protocolC中的UDP服务器/客户端-sendto错误:协议不支持地址系列
【发布时间】:2017-03-29 15:48:04
【问题描述】:

我正在编写一个简单的说明性 UDP 服务器客户端。服务器应根据客户端输入计算客户端的网络字节顺序,并将其发送回客户端。我一整天都在尝试修复这个错误。任何帮助将不胜感激。这是服务器启动和输出:

./udpserver.out 4545
from.sin_family: 12079
Little Endian

ERROR UDP_SRV_004 - can not send message to client: Address family not supported by protocol

客户端启动:

./udpclient.out localhost 4545

服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#define BUFFER_SIZE 80

static void error(char *msg){
    perror(msg);
    exit(1);
}


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

    int socketfd, portno, fromlen, n, length;
    struct sockaddr_in server, from;
    char buffer[BUFFER_SIZE];
    char response[BUFFER_SIZE];
    const char *le = "Little Endian\n";
    const char *be = "Big Endian\n";
    const char *unk = "Unknown \n";
    int cli_family;

    if(argc!=2){
        fprintf(stderr,"Error starting the client, please start server as: %s port\n", argv[0]);
        exit(0);
    }

    if((socketfd=socket(AF_INET,SOCK_DGRAM,0))<0)
        error("ERROR UDP_SRV_001 - can not create socket");

    portno=atoi(argv[1]);

    bzero(&server,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(portno);

    if(bind(socketfd,(struct sockaddr *)&server,sizeof(server))<0)
        error("ERROR UDP_SRV_002 - can not bind server address to a socket file descriptor");

    bzero(buffer, BUFFER_SIZE);
    bzero(response,BUFFER_SIZE);
    n=recvfrom(socketfd,buffer,BUFFER_SIZE-1,0,(struct sockaddr *)&from,&fromlen);
    if (n<0)
        error("ERROR UDP_SRV_003 - can not receive client's message");

    cli_family=from.sin_family;
    printf("from.sin_family: %d\n", cli_family); 

    if (buffer[0] == 1 && buffer[1] == 2)
        strcpy(response, be);
    else if (buffer[0] == 2 && buffer[1] == 1)
        strcpy(response, le);
    else
        strcpy(response, unk);

    length=strlen(response);
    printf("%s\n",response);

    n=sendto(socketfd,response,length,0,(struct sockaddr *)&from,fromlen);
    if(n<0)
        error("ERROR UDP_SRV_004 - can not send message to client");


    return 0;
}

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 80

static void error(char *msg){
    perror(msg);
    exit(1);
}

typedef union UNIT{
        short s;
        char c[sizeof(short)];
    }unit;

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

    int socketfd, portno, n, length;
    struct sockaddr_in  server, from;
    struct hostent *host;
    char buffer[BUFFER_SIZE];
    unit un;
    un.s=0x0102;

    if(argc!=3){
        fprintf(stderr,"Error starting the client, please start client as: %s hostname port\n", argv[0]);
        exit(0);
    }

    if((host=gethostbyname(argv[1]))==NULL)
        error("ERROR UDP_CLI_001 - no such host defined, please check your /etc/hosts file");

    portno=atoi(argv[2]);

    if((socketfd=socket(AF_INET,SOCK_DGRAM,0))<0)
        error("ERROR UDP_CLI_002 - can not create socket");

    bzero((char *)&server,sizeof(server));

    server.sin_family=AF_INET;
    server.sin_port=htons(portno);
    bcopy((char *)host->h_addr,(char *)&server.sin_addr,host->h_length);
    length=sizeof(server);

    if(sizeof(short)!=2){
      exit(0);
    }

    n=sendto(socketfd,un.c,strlen(un.c),0,(struct sockaddr *)&server,length);
    if(n<0)
        error("ERROR UDP_CLI_003 - can not send to server");
    bzero(buffer,BUFFER_SIZE);
    n=recvfrom(socketfd,buffer,BUFFER_SIZE,0,(struct sockaddr *)&from, &length);
    if(n<0)
        error("ERROR UDP_CLI_004 - can receive from server");
    printf("%s",buffer);


return 0;
}

【问题讨论】:

  • 服务器接收的客户端 sin_family 对于 AF_INET 应该是 2。但是由于某种原因它搞砸了,我无法修复它,因为如果我尝试用 AF_INET 覆盖它,它会给我发送到无效参数错误。

标签: c sockets unix udp sendto


【解决方案1】:

在服务器中,您未能在调用 sendto 之前初始化 fromlen。因此,from 未填充。

来自man page

如果 src_addr 不为 NULL,并且底层协议提供 源地址,填写这个源地址。当src_addr为 NULL,什么都不填;在这种情况下,不使用 addrlen,并且 也应该为 NULL。 参数 addrlen 是一个值结果参数, 调用者应该在调用之前初始化它的大小 与 src_addr 关联的缓冲区,并在返回时修改以指示 源地址的实际大小。返回的地址是 如果提供的缓冲区太小,则截断;在这种情况下,addrlen 将返回一个大于提供给调用的值。

recvfrom 函数尝试读取fromlen 的值,但由于它未初始化,因此您调用undefined behavior

初始化fromlen,这将解决问题。

fromlen = sizeof from;
n=recvfrom(socketfd,buffer,BUFFER_SIZE-1,0,(struct sockaddr *)&from,&fromlen);

【讨论】:

  • 不错的捕获 dbush。 @Milos - 你应该已经看到了一个编译器警告。不要忽视这些。
  • @BrianMcFarland 是的,忘了把 -Wall 放在 Makefile 中,这就是为什么我没有看到任何警告。
  • @MilosMilosavljevic 我没有看到-Wall -Wextra 的任何警告,尽管我使用的是旧版本的 gcc。然而,像 valgrind 这样的工具会立即抓住它。
  • @dbush 是的,你是对的,即使 -Wall -Wextra 也没有抓住它。但是他们给我打印了几个警告,所以我认为这个也应该在那里。将立即查找 valgrind。
猜你喜欢
  • 2019-02-01
  • 1970-01-01
  • 2012-12-03
  • 2018-11-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 2010-10-01
  • 1970-01-01
相关资源
最近更新 更多