嵌入式技术论坛
直播中

贾伟刚

8年用户 1673经验值
私信 关注
[经验]

浅析RT-Thread 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引用关系




解决办法
解决方案要基于前篇文章2021/07/14 LwIP 2.0.2 多网卡 ping;LwIP 指定网卡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-threadcomponents
etsal_socketsrcsal_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
#include  /* 使用BSD socket,需要包含socket.h头文件 */
#include
#include
#include
#include
#include
#define LOG_TAG     "test_multi_netif"
#define LOG_LVL     LOG_LVL_DBG
#include
#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!
", 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!
");
        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
");
    }
    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] = '';
        /* 在控制终端显示收到的数据 */
        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.
", 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.
");
        rt_thread_startup(tid1);
    }
    else
    {
        return -1;
    }
    if (str != RT_NULL)
    {
        rt_free(str);
    }
    return 0;
}
#ifdef FINSH_USING_MSH
#include
MSH_CMD_EXPORT(net_start, a tcp client test);
#endif
结果
腾讯云服务器:
nc -l 9008 > test.log
本地服务器
MSH

原作者:yukelab

更多回帖

发帖
×
20
完善资料,
赚取积分