RT-Thread 按指定网卡创建socket

发布于 2021-07-16 16:32:36    浏览:142

socket 无法指定多网卡原因

原因

下面流程导致创建多网卡 socket 无法指定网卡
在 sal_socket 时,在 socket_init 中绑定了网卡为 default,然后通过 网卡下面的 protocol family socket 创建网卡 pf->skt_ops->socket(domain, type, protocol)

int sal_socket(int domain, int type, int protocol)
{
    int retval;
    int socket, proto_socket;
    struct sal_socket *sock;
    struct sal_proto_family *pf;

    /* allocate a new socket and registered socket options */
    socket = socket_new();
    if (socket < 0)
    {
        return -1;
    }

    /* get sal socket object by socket descriptor */
    sock = sal_get_socket(socket);
    if (sock == RT_NULL)
    {
        socket_delete(socket);
        return -1;
    }

    /* Initialize sal socket object */
    retval = socket_init(domain, type, protocol, &sock);
    if (retval < 0)
    {
        LOG_E("SAL socket protocol family input failed, return error %d.", retval);
        socket_delete(socket);
        return -1;
    }

    /* valid the network interface socket opreation */
    SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, socket);

    proto_socket = pf->skt_ops->socket(domain, type, protocol);
    if (proto_socket >= 0)
    {
#ifdef SAL_USING_TLS
        if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, socket))
        {
            sock->user_data_tls = proto_tls->ops->socket(socket);
            if (sock->user_data_tls == RT_NULL)
            {
                socket_delete(socket);
                return -1;
            }
        }
#endif
        sock->user_data = (void *) proto_socket;
        return sock->socket;
    }
    socket_delete(socket);
    return -1;
}

创建不同协议类型的socket引用关系

df796d02cba442799a85d97acc40546d.png
5cc036d1856c4da39fab8993c649543f.png
5d3cbe16021b4829b3edd7e596ce3ac3.png
158444d745a943a8b38c984bc8f590da.png

解决办法

解决方案要基于前篇文章2021/07/14 LwIP 2.0.2 多网卡 pingLwIP 指定网卡ping
在创建 socket 的过程中,通过 sin_family 指定使用 AF_WIZ, AF_AT, AF_INET(lwip), 然后通过 bind 函数,二次选择网卡;

socket(AF_INET, SOCK_STREAM, 0)
  • wiznet

family = AF_WIZ
sec_family = AF_INET

  • lwip

family=AF_INET
sec_family = AF_UNSPEC

  • at device

family=AF_AT
sec_family = AF_INET

修改过程

修改函数 socket_init

rt-thread\components\net\sal_socket\src\sal_socket.c
修改函数,通过 sin_family 指定使用 AF_WIZ, AF_AT, AF_INET(lwip);

static int socket_init(int family, int type, int protocol, struct sal_socket **res)  // 420 行
/**
 * This function will initialize sal socket object and set socket options
 *
 * @param family    protocol family
 * @param type      socket type
 * @param protocol  transfer Protocol
 * @param res       sal socket object address
 *
 * @return  0 : socket initialize success
 *         -1 : input the wrong family
 *         -2 : input the wrong socket type
 *         -3 : get network interface failed
 */
static int socket_init(int family, int type, int protocol, struct sal_socket **res)
{

    struct sal_socket *sock;
    struct sal_proto_family *pf;
    struct netdev *netdv_def = netdev_default;
    struct netdev *netdev = RT_NULL;
    rt_bool_t flag = RT_FALSE;

    if (family < 0 || family > AF_MAX)
    {
        return -1;
    }

    if (type < 0 || type > SOCK_MAX)
    {
        return -2;
    }

    sock = *res;
    sock->domain = family;
    sock->type = type;
    sock->protocol = protocol;

    netdv_def = netdev_get_by_family(family);
    if (netdv_def == RT_NULL)
    {
        netdv_def = netdev_default;
    }

    if (netdv_def && netdev_is_up(netdv_def))
    {
        /* check default network interface device protocol family */
        pf = (struct sal_proto_family *) netdv_def->sal_user_data;
        if (pf != RT_NULL && pf->skt_ops && (pf->family == family || pf->sec_family == family))
        {
            sock->netdev = netdv_def;
            flag = RT_TRUE;
        }
    }

    if (flag == RT_FALSE)
    {
        /* get network interface device by protocol family */
        netdev = netdev_get_by_family(family);
        if (netdev == RT_NULL)
        {
            LOG_E("not find network interface device by protocol family(%d).", family);
            return -3;
        }

        sock->netdev = netdev;
    }
    LOG_I("use network interface: %s", sock->netdev->name);

    return 0;
}

测试多网卡功能

#include <rtthread.h>
#include  <sys/socket.h>/* 使用BSD socket,需要包含socket.h头文件 */
#include <netdb.h>
#include <stdio.h>
#include <string.h>

#include 
#include <netdev.h>

#define LOG_TAG     "test_multi_netif" 
#define LOG_LVL     LOG_LVL_DBG
#include <ulog.h>

#define BUFSZ   256
#define SEND_DATASZ 256
#define SAY_BUFSZ   240
#define THREAD_PRIORITY 20
#define THREAD_STACK_SIZE 2048
#define THREAD_TIMESLICE 5

#define EVENT_CLOSE_SOCK 0x01

#define SERVER_E0 "139.155.51.105"
#define SERVER_W0 "172.18.44.66"

static int close_socket(int *sock);
static char send_data[80] = {0}; /* 发送用到的数据 */

typedef struct TEST_MULTI_NETIF
{
    char netdev_name[8];
    int sock;
    int netdev_type;
    rt_event_t event;
}TCP_CLIENT;

TCP_CLIENT tcp_e0;
TCP_CLIENT tcp_w0;

static int socket_connect(const char *dest, struct netdev *dev)
{
    struct hostent *host;
    struct sockaddr_in server_addr;
    int sock = -1;
    int port = 9008;
    struct sockaddr_in local;
    /* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */
    host = gethostbyname(dest);

    /* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        /* 创建socket失败 */
        log_e("create socket error");

        /* 释放接收缓冲 */
        /* rt_free(recv_data); */
        return -1;
    }

    log_i("create success");

    /* 初始化预连接的服务端地址 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr = *((struct in_addr *)host->h_addr);
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

    local.sin_len = sizeof(local);
    local.sin_family = AF_INET;
    local.sin_port = 0;
    local.sin_addr.s_addr = dev->ip_addr.addr;
    if (dev->ip_addr.addr != 0)
    {
        bind(sock, (struct sockaddr *)&local, sizeof(struct sockaddr_in));
    }
    log_i("bind %x success", local.sin_addr.s_addr);

    /* 连接到服务端 */
    if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != RT_EOK)
    {
        /* 连接失败 */
        log_e("Connect server [%s] failed!\n", dest);
        closesocket(sock);
        return -1;
    }
    log_i("connect server success");
    return sock;
}

static int close_socket(int *sock)
{
    if (*sock > 0)
    {
        log_d("disconnect server!\n");
        closesocket(*sock);
        *sock = -1;
    }
}


/* create receive thread */
static void thread_rece_entry(void* parameter)
{
    char *recv_data;
    int bytes_received;
    TCP_CLIENT *client = (TCP_CLIENT *)parameter;
        /* int no = (int) parameter; #<{(| 获得线程的入口参数 |)}># */
    /* 分配用于存放接收数据的缓冲 */
    recv_data = rt_malloc(BUFSZ);
    if (recv_data == RT_NULL)
    {
        rt_kprintf("No memory\n");
    }
    while(1)
    {
        /* 从sock连接中接收最大BUFSZ - 1字节数据 */
        bytes_received = recv(client->sock, recv_data, BUFSZ - 1, 0);
        if (bytes_received < 0)
        {
                /* 接收失败,关闭这个连接 */
                log_d("received less than zero, close the socket.");
                rt_free(recv_data);
                rt_event_send(client->event, EVENT_CLOSE_SOCK);
                return;
        }
        else if (bytes_received == 0)
        {
                /* 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接 */
                log_d("received equal zero, close the socket.");
                rt_free(recv_data);
                rt_event_send(client->event, EVENT_CLOSE_SOCK);
                return;
        }

        recv_data[bytes_received] = '\0';
        /* 在控制终端显示收到的数据 */
        log_i("recv:%s", recv_data);
    }
}


// static void send_chat_data(char *p_send, int len)
static void thread_send_entry(void* parameter)
{
    int ret;
    char *send_str = rt_calloc(20, 1);
    rt_thread_t tid;
    TCP_CLIENT *client = (TCP_CLIENT *)parameter;
    rt_uint32_t e;
    char *thread_name = rt_calloc(8, 1);

    sprintf(send_str, "I am netif %s", client->netdev_name);
    if (client->sock >= 0)
    {
    #if 1
        sprintf(thread_name, "Recv_%s", client->netdev_name);
        tid = rt_thread_create(thread_name,
        thread_rece_entry, (void*)parameter, /* 线程入口是thread_entry, 入口参数是1 */
        THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
        if (tid != RT_NULL)
        {
            log_d("Create receive thread %s receive.\n", client->netdev_name);
            rt_thread_startup(tid);
        }
        else
        {
            if (thread_name != RT_NULL)
            {
                rt_free(thread_name);
            }
            if (send_str != RT_NULL)
            {
                rt_free(send_str);
            }
            return;
        }
    #endif
    }

    if (thread_name != RT_NULL)
    {
        rt_free(thread_name);
    }
    log_i("socket %s id: %d", client->netdev_name, client->sock);

    while(1)
    {
    #if 1
        if (rt_event_recv(client->event, EVENT_CLOSE_SOCK,
                        RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                        RT_WAITING_NO, &e) == RT_EOK)
        {
            if (EVENT_CLOSE_SOCK == (e & EVENT_CLOSE_SOCK))
            {
                close_socket(&client->sock);
                if (send_str != RT_NULL)
                {
                    rt_free(send_str);
                }
                return;
            }
        }
    #endif

        if (client->sock >= 0)
        {
            ret = send(client->sock, send_str, strlen(send_str), 0);
            if (ret < 0)
            {
                log_e("send error,close the socket.");
            }
            else if (ret == 0)
            {
                log_e("Send warning,send function return 0.");
            }
        }

        rt_thread_mdelay(1000);
    }
}


static int net_start(int argc, char *argv[])
{
    rt_thread_t tid1;
    TCP_CLIENT *client = RT_NULL;
    char *server = RT_NULL;
    int index = 0;
    char *str = rt_calloc(10, 1);
    /* 是否创建Socket */
    if (argc == 1)
    {
        memcpy(tcp_e0.netdev_name, "e0", 2);
        client = &tcp_e0;
        server = SERVER_E0;
        index = 0;
    }
    else
    {
        memcpy(tcp_w0.netdev_name, "w0", 2);
        client = &tcp_w0;
        server = SERVER_W0;
        index = 1;
    }

    if (client->sock >= 0)
    {
        log_i("socket already exist");
    }

    if ((client->sock = socket_connect(server, netdev_get_by_name(client->netdev_name))) < 0)
    {
        log_e("create and connect server error.");
        return -1;
    }

    sprintf(str, "e_%s", client->netdev_name);
    client->event = rt_event_create(str, RT_IPC_FLAG_FIFO);

    rt_memset(str, 0, 10);
    sprintf(str, "send_%s", client->netdev_name);
    tid1 = rt_thread_create(str, thread_send_entry, (void*)(client), /* 线程入口是thread_entry, 入口参数是1 */
    THREAD_STACK_SIZE, THREAD_PRIORITY + index, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)
    {
        log_d("Create receive thread for chat receive.\n");
        rt_thread_startup(tid1);
    }
    else
    {
        return -1;
    }
    if (str != RT_NULL)
    {
        rt_free(str);
    }
    return 0;
}

#ifdef FINSH_USING_MSH
#include <finsh.h> 
MSH_CMD_EXPORT(net_start, a tcp client test);
#endif

结果

腾讯云服务器:

nc -l 9008 > test.log

7315451e825c4caf8e1f677e721578d1.png

本地服务器

c7dbe5b892a04096a4d31f6bb653430d.png

MSH

7638581fa2e54278bb271cb56fd595cb.png
21148993e5fe4525ae14734fb420ae90.png

7 条评论

发布
问题