udp socket - 绑定并连接成功但发送不起作用

时间:2015-12-31 10:56:37

标签: c sockets network-programming udp

我试图通过两个对等体之间的连接udp-socket进行通信。对等体之间的地址信息通过服务器使用tcp传输。

首先,每个对等体设置一个udp-socket,绑定一个地址,然后通过tcp将地址信息发送到服务器。服务器将连接信息发送给另一个对等体。

当对等方收到信息时,它会尝试连接' udp-socket到另一个对等体。连接调用成功,但send给出了以下错误:' errno:89,需要目标地址'。

peer.c:

#include "Socket.h"
#include "function.h"

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

if(argc != 4) {
    printf("3 Parameter must be given.\nclient-ip server-ip server-port\n");
    exit(-1);
}

struct sockaddr_in my_addr, server_addr, other_peer_addr;

address_info* msg_address_info;
header *msg;

int recv_done = 0;
int optval = 1;
int fd_udp, fd_server;
ssize_t len;
socklen_t my_addr_len;
fd_set rfds;

FD_ZERO(&rfds);

fd_udp = Socket(AF_INET, SOCK_DGRAM, 0);

memset((void *) &my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
    my_addr.sin_len = sizeof(struct sockaddr_in);
#endif
my_addr.sin_port = 0; // any port
if ((my_addr.sin_addr.s_addr = (in_addr_t)inet_addr(argv[1])) == INADDR_NONE) {
    fprintf(stderr, "Invalid address\n");
}

Bind(fd_udp, (const struct sockaddr *) &my_addr, sizeof(my_addr));

Setsockopt(fd_udp, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
Setsockopt(fd_udp, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(int));

memset((void *) &my_addr, 0, sizeof(my_addr));
my_addr_len = sizeof(my_addr);
//get the current address for server registration
Getsockname(fd_udp, (struct sockaddr *) &my_addr, &my_addr_len);


/* TCP Communication */
/* i use 127.0.0.1:55555 for the server */
fd_server = Socket(AF_INET, SOCK_STREAM, 0);

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
     server_addr.sin_len = sizeof(struct sockaddr_in);
#endif
server_addr.sin_port = htons(atoi(argv[3]));
if ((server_addr.sin_addr.s_addr = (in_addr_t) inet_addr(argv[2]))
        == INADDR_NONE) {
    fprintf(stderr, "Invalid address\n");
}

Connect(fd_server, (const struct sockaddr *) &server_addr, sizeof(server_addr));

len = sizeof(address_info);

msg_address_info = malloc(len + get_padding(len));

memset((void*)msg_address_info, 0, len + get_padding(len));

msg_address_info->head.type = htons(30);
msg_address_info->head.length = htons(sizeof(address_info));
msg_address_info->ip = my_addr.sin_addr.s_addr;
msg_address_info->port = my_addr.sin_port;

Send(fd_server, msg_address_info, len + get_padding(len), 0);

free(msg_address_info);

while(!recv_done) {

    FD_ZERO(&rfds);
    FD_SET(fd_server, &rfds);

    //data is ready for recv
    if(FD_ISSET(fd_server, &rfds)) {

        msg = recv_stream(fd_server);
        if(msg != NULL) {
            if(ntohs(msg->type) == 3) {
                Close(fd_server);
                recv_done = 1;

                msg_address_info = (address_info *) msg;

                other_peer_addr.sin_addr.s_addr = msg_address_info->ip;
                other_peer_addr.sin_port = msg_address_info->port;
            }
        }
    }
}

char buf[512];
memset((void*)&buf, 0, 512);

char* other_peer_ip;
int other_peer_port;

other_peer_ip = inet_ntoa(other_peer_addr.sin_addr);
other_peer_port = ntohs(other_peer_addr.sin_port);

printf("other_peer ip: %s\nother_peer port: %i\n", other_peer_ip, other_peer_port); //matches on bothe peer's

int ret_con = connect(fd_udp, (const struct sockaddr *) &other_peer_addr, sizeof(other_peer_addr));
fprintf(stderr, "ret_con: %i, errno: %i, %s\n", ret_con, errno, strerror(errno));

int ret_send = send(fd_udp, buf, 512, 0);
if(ret_send < 0) {
    fprintf(stderr, "ret_send: %i, errno: %i, %s\n", ret_send, errno, strerror(errno));
}

}

function.h:

#define BUFFER_SIZE  (1<<16)

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

#include <errno.h>

#include "Socket.h"

typedef struct {
    uint16_t type;
    uint16_t length;
} header;

typedef struct {
    header head;
    uint32_t ip;
    uint16_t port;
} address_info;

int get_padding(int length);
void* recv_stream(int fd);

functions.c:

#include "functions.h"

void* recv_stream(int fd) {
    if(fd < 0) {
        fprintf(stderr, "recv_stream: Invaild fd\n");
        return NULL;
    }
    ssize_t len;
    int msg_length;
    char buf[BUFFER_SIZE];
    char* msg;

    len = recv(fd, &buf, BUFFER_SIZE, MSG_PEEK);

    //Client has closed the connection
    if(len <= 0) {
        fprintf(stderr, "recv_stream: Client closed the connection.\n");
        exit(-1);
    }

#ifdef DEBUG
    printf("PEEKED %zd bytes.\n", len);
#endif

    if(len < sizeof(header)) {
        fprintf(stderr, "recv_stream: Message to small no header\n");
        return NULL;
    }

    header *head = (header *) buf;

    msg_length = ntohs(head->length);

    if(len < msg_length) {
        fprintf(stderr, "recv_stream: Message to small\n");
        return NULL;
    }
    else if(len >= msg_length + get_padding(msg_length)) {
        msg = malloc(msg_length + get_padding(msg_length));

        len = Recv(fd, msg, msg_length + get_padding(msg_length), 0);

        head = (header *) msg;
    }

    return head;
}


int get_padding(int length) {
    if(length <= 0) {
        fprintf(stderr, "get_padding: wrong length");
    }

    int pad = length % 4;
    if(pad == 3)
        pad = 1;
    else if(pad == 1)
        pad = 3;

    return pad;
}

带有Wrapper函数的Socket.c

int Socket(int fd, int type, int protocol) {

    int n;
    if((n=socket(fd,type,protocol)) < 0) {
        perror("socket");
        exit(-1);
    }
    return n;
}

/* many more */

我已经阅读了问题Can you bind() and connect() both ends of a UDP connection,但它没有解决我的问题。

地址信息的传输似乎是正确的。我在两个对等点上打印了发送和接收的地址,但它们匹配。

我对这个问题很着迷,无法弄清楚我的错误。你能救我吗?

编辑: 提供了新的例子

现在我收到以下错误:

ret_con: -1, errno: 97, Address family not supported by protocol
ret_send: -1, errno: 89, Destination address required

1 个答案:

答案 0 :(得分:1)

在UDP套接字上调用sin_family之前,您没有填充other_peer_addr的{​​{1}}字段。您只填充connect()sin_addr字段,这还不够。需要告诉sin_port传递给它的地址类型,并且必须使用与套接字相同的族(就像connect()一样)。由于您没有填充bind()字段,因此它包含堆栈中的随机值,这导致sin_family失败,而#34;地址系列不受支持&#34;错误,并且无法在未连接的套接字上调用connect(),从而导致需要&#34;目标地址&#34;错误。