udp 套接字 - 绑定和连接成功但发送不起作用

udp socket - bind and connect succeed but send does not work

我正在尝试通过连接的 udp 套接字在两个对等点之间进行通信。对等点之间的地址信息是通过服务器使用 tcp 传输的。

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

当对等点收到信息时,它会尝试 'connect' 将 udp-socket 连接到另一个对等点。连接调用成功,但发送给我以下错误:'errno: 89, Destination address required'.

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;
}

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

在 UDP 套接字上调用 connect() 之前,您没有填充 other_peer_addrsin_family 字段。您仅填充 sin_addrsin_port 字段,这是不够的。 connect() 需要被告知传递给它的地址类型,并且必须使用与套接字相同的系列(就像 bind() 一样)。由于您没有填充 sin_family 字段,它包含来自堆栈的随机值,这导致 connect() 失败并出现 "Address family not supported" 错误,并且 send() 不能在未连接的套接字上调用,从而导致 "Destination address required" 错误。