LWIP的Socket接口

news2025/5/21 4:29:35

Socket接口简介

类似于文件操作的一种网络连接接口,通常将其称之为“套接字”。lwIPSocket接口兼容BSD Socket接口,但只实现完整Socket的部分功能

netconn是对RAW的封装

Socket是对netconn的封装

SOCKET结构体

struct sockaddr {
u8_t sa_len; /* 长度 */
sa_family_t sa_family; /* 协议簇 */
char sa_data[14]; /* 连续的 14 字节信息 */
};
struct sockaddr_in {
u8_t sin_len; /* 长度 */
u8_t sin_family; /* 协议簇 */
u16_t sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP 地址 */
char sin_zero[8];
}

为啥用两个结构体

sockaddr的缺陷是:sa_zero把目标地址和端口信息混在了一起

sockaddr_in该结构体解决了sockaddr的缺陷,把port和addr分开存储在两个变量中

NETCONN相关函数

SOCKET API

描述

scoket

创建一个scoket连接

bind

服务器端绑定套接字与网卡信息

connect

Socket 与远程 IP 地址和端口号绑定

listen

监听连接

accept

断开连接

read/recv/recvfrom

监听连接(只在TCP服务器)

sendto/send/write

获取一个TCP连接(只在TCP服务器)

close

关闭连接

Socket 编程 UDP 连接流程
 

实现 UDP 协议之前, 用户必须先配置结构体 sockaddr_in 的成员变量才能实现 UDP 连接,该配置步骤如下所示:

① sin_family 设置为 AF_INET 表示 IPv4 网络协议。

② sin_port 为设置端口号, 笔者设置为 8080。

③ sin_addr.s_addr 设置本地 IP 地址。

④ 调用函数 Socket 创建 Socket 连接,注意: 该函数的第二个参数 SOCK_STREAM 表

示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。

⑤ 调用函数 bind 将本地服务器地址与 Socket 进行绑定。

⑥ 调用收发函数接收或者发送。

实现连接的主要函数


/* 需要自己设置远程IP地址 */
#define IP_ADDR   "192.168.1.111"

#define LWIP_DEMO_RX_BUFSIZE         200    /* 最大接收数据长度 */
#define LWIP_DEMO_PORT               8080   /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO       ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 */

/* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 发送数据内容 */
char g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
struct sockaddr_in g_local_info;              /* 定义Socket地址信息结构体 */
socklen_t g_sock_fd;                          /* 定义一个Socket接口 */
static void lwip_send_thread(void *arg);

extern QueueHandle_t g_display_queue;         /* 显示消息队列句柄 */

/**
 * @brief       发送数据线程
 * @param       无
 * @retval      无
 */
void lwip_data_send(void)
{
    sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    BaseType_t lwip_err;
    char *tbuf;
    lwip_data_send();                                   /* 创建发送数据线程 */
    memset(&g_local_info, 0, sizeof(struct sockaddr_in)); /* 将服务器地址清空 */
    g_local_info.sin_len = sizeof(g_local_info);
    g_local_info.sin_family = AF_INET;                    /* IPv4地址 */
    g_local_info.sin_port = htons(LWIP_DEMO_PORT);        /* 设置端口号 */
    g_local_info.sin_addr.s_addr = htons(INADDR_ANY);     /* 设置本地IP地址 */

    g_sock_fd = socket(AF_INET, SOCK_DGRAM, 0);           /* 建立一个新的socket连接 */
    
    tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */
    sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */
    lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);
    
    /* 建立绑定 */
    bind(g_sock_fd, (struct sockaddr *)&g_local_info, sizeof(struct sockaddr_in));

    while (1)
    {
        memset(g_lwip_demo_recvbuf, 0, sizeof(g_lwip_demo_recvbuf));
        recv(g_sock_fd, (void *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0);
        
        lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);
        
        if (lwip_err == errQUEUE_FULL)
        {
            printf("队列Key_Queue已满,数据发送失败!\r\n");
        }
    }
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;
    
    g_local_info.sin_addr.s_addr = inet_addr(IP_ADDR);                /* 需要发送的远程IP地址 */

    while (1)
    {
        if ((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA)     /* 有数据要发送 */
        {
            sendto(g_sock_fd,                                         /* scoket */
                  (char *)g_lwip_demo_sendbuf,                        /* 发送的数据 */
                  sizeof(g_lwip_demo_sendbuf), 0,                     /* 发送的数据大小 */
                  (struct sockaddr *)&g_local_info,                   /* 接收端地址信息 */ 
                  sizeof(g_local_info));                              /* 接收端地址信息大小 */

            g_lwip_send_flag &= ~LWIP_SEND_DATA;
        }
        
        vTaskDelay(100);
   }
}

Socket 编程 UDP 组播实验

必须对以下保证设置

\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_eth.c

static void ETH_MACDMAConfig(ETH_HandleTypeDef *heth, uint32_t err)

  macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE;  //使能 全部接收

 macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;//不过滤 组播的帧

\Middlewares\lwip\arch\lwipopts.h

保证设置1

\Middlewares\LWIP\arch\ethernetif.c

static void low_level_init(struct netif *netif)

必须保证添加如下所有标志位

netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP|NETIF_FLAG_IGMP;   /*广播 ARP协议 链接检测*/

组播的主要实现函数



/* socket信息 */
struct link_socjet_info
{
    struct sockaddr_in client_addr; /* 网络地址信息 */
    socklen_t client_addr_len;      /* 网络地址信息长度 */
    int optval;                     /* 为存放选项值 */
    int sfd;                        /* socket控制块 */
    ip_mreq multicast_mreq;         /* 组播控制块 */
    
    struct
    {
        uint8_t *buf;               /* 缓冲空间 */
        uint32_t size;              /* 缓冲空间大小 */
    } send;                         /* 发送缓冲 */
    
    struct
    {
        uint8_t *buf;               /* 缓冲空间 */
        uint32_t size;              /* 缓冲空间大小 */
    } recv;                         /* 接收缓冲 */
};

/* 多播信息 */
struct ip_mreq_t
{
    struct ip_mreq mreq;            /* 多播信息控制块 */
    socklen_t mreq_len;             /* 多播信息长度 */
};

#define LWIP_SEND_THREAD_PRIO       (tskIDLE_PRIORITY + 3) /* 发送数据线程优先级 */
void lwip_send_thread(void *pvParameters);
/* 接收数据缓冲区 */
static uint8_t g_lwip_demo_recvbuf[1024];
static uint8_t g_lwip_demo_sendbuf[] = {"ALIENTEK DATA\r\n"}; 

/* 多播 IP 地址 */
#define GROUP_IP "224.0.1.0"

/**
 * @brief       测试代码
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    struct link_socjet_info *socket_info;
    struct ip_mreq_t *mreq_info;
    
    socket_info = mem_malloc(sizeof(struct link_socjet_info));
    mreq_info = mem_malloc(sizeof(struct ip_mreq_t));
    
    socket_info->sfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if (socket_info->sfd < 0)
    {
        printf("socket failed!\n");
    }
    
    socket_info->client_addr.sin_family = AF_INET;
    socket_info->client_addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* 待与 socket 绑定的本地网络接口 IP */   
    socket_info->client_addr.sin_port = htons(9999);                /* 待与 socket 绑定的本地端口号 */
    socket_info->client_addr_len = sizeof(socket_info->client_addr);
    
    /* 设置接收和发送缓冲区 */
    socket_info->recv.buf = g_lwip_demo_recvbuf;
    socket_info->recv.size = sizeof(g_lwip_demo_recvbuf);
    socket_info->send.buf = g_lwip_demo_sendbuf;
    socket_info->send.size = sizeof(g_lwip_demo_sendbuf);
    
    /* 将 Socket 与本地某网络接口绑定 */
    int ret = bind(socket_info->sfd, (struct sockaddr*)&socket_info->client_addr, socket_info->client_addr_len);
    
    if (ret < 0)
    {
        printf(" bind error!\n ");
    }

    mreq_info->mreq.imr_multiaddr.s_addr = inet_addr(GROUP_IP);     /* 多播组 IP 地址设置 */
    mreq_info->mreq.imr_interface.s_addr = htonl(INADDR_ANY);       /* 待加入多播组的 IP 地址 */
    mreq_info->mreq_len = sizeof(struct ip_mreq);

    /* 添加多播组成员(该语句之前,socket 只与 某单播IP地址相关联 执行该语句后 将与多播地址相关联) */
    ret = setsockopt(socket_info->sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq_info->mreq,mreq_info->mreq_len);
    
    if (ret < 0)
    {
        printf("setsockopt failed !");
    }
    else
    {
        printf("setsockopt success\n");
    }
    
    int length = 0;
    struct sockaddr_in sender;
    int sender_len = sizeof(sender);
    
    sys_thread_new("lwip_send_thread", lwip_send_thread, (void *)socket_info, 512, LWIP_SEND_THREAD_PRIO );
    
    while(1)
    {
        length = recvfrom(socket_info->sfd,socket_info->recv.buf,socket_info->recv.size,0,(struct sockaddr*)&sender,(socklen_t *)&sender_len);
        socket_info->recv.buf[length]='\0';
        printf("%s %d : %s\n", inet_ntoa(sender.sin_addr), ntohs(sender.sin_port), socket_info->recv.buf);
        vTaskDelay(10);
    }
    
    setsockopt(socket_info->sfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq_info->mreq, mreq_info->mreq_len);
    closesocket(socket_info->sfd);
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入struct link_socjet_info结构体
 * @retval      无
 */
void lwip_send_thread(void *pvParameters)
{
    struct link_socjet_info *socket_info = pvParameters;
    socket_info->client_addr.sin_addr.s_addr = inet_addr(GROUP_IP); /* 组播ip */
    
    while (1)
    {
        /* 数据广播 */
        sendto(socket_info->sfd, socket_info->send.buf, socket_info->send.size + 1, 0, (struct sockaddr*)&socket_info->client_addr,socket_info->client_addr_len);
        vTaskDelay(1000);
    }
}

Socket 编程 TCP 客户端流程
 

实现 TCP 客户端之前,用户必须先配置结构体 sockaddr_in 的成员变量才能实现TCPClient 连接,该配置步骤如下所示:

① sin_family 设置为 AF_INET 表示 IPv4 网络协议。

② sin_port 为设置端口号。

③ sin_addr.s_addr 设置远程 IP 地址。

④ 调用函数 Socket 创建 Socket 连接, 注意: 该函数的第二个参数 SOCK_STREAM 表

示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。

⑤ 调用函数 connect 连接远程 IP 地址。

⑥ 调用收发函数实现远程通讯。

Socket 接口的 TCPClient 实验
 

实现的主要代码



/* 设置远程IP地址 */
//#define DEST_IP_ADDR0               192
//#define DEST_IP_ADDR1               168
//#define DEST_IP_ADDR2                 1
//#define DEST_IP_ADDR3               167

/* 需要自己设置远程IP地址 */
#define IP_ADDR   "192.168.1.37"

#define LWIP_DEMO_RX_BUFSIZE         100                        /* 最大接收数据长度 */
#define LWIP_DEMO_PORT               8080                       /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO       ( tskIDLE_PRIORITY + 3 )    /* 发送数据线程优先级 */
/* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 

/* 发送数据内容 */
uint8_t g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
int g_sock = -1;
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);

extern QueueHandle_t g_display_queue;     /* 显示消息队列句柄 */

/**
 * @brief       发送数据线程
 * @param       无
 * @retval      无
 */
void lwip_data_send(void)
{
    sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    struct sockaddr_in atk_client_addr;
    err_t err;
    int recv_data_len;
    BaseType_t lwip_err;
    char *tbuf;
    
    lwip_data_send();                                           /* 创建发送数据线程 */
    
    while (1)
    {
sock_start:
        g_lwip_connect_state = 0;
        atk_client_addr.sin_family = AF_INET;                   /* 表示IPv4网络协议 */
        atk_client_addr.sin_port = htons(LWIP_DEMO_PORT);       /* 端口号 */
        atk_client_addr.sin_addr.s_addr = inet_addr(IP_ADDR);   /* 远程IP地址 */
        g_sock = socket(AF_INET, SOCK_STREAM, 0);                 /* 可靠数据流交付服务既是TCP协议 */
        memset(&(atk_client_addr.sin_zero), 0, sizeof(atk_client_addr.sin_zero));
        
        tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */
        sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */
        lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);
        
        /* 连接远程IP地址 */
        err = connect(g_sock, (struct sockaddr *)&atk_client_addr, sizeof(struct sockaddr));
      
        if (err == -1)
        {
            printf("连接失败\r\n");
            g_sock = -1;
            closesocket(g_sock);
            myfree(SRAMIN, tbuf);
            vTaskDelay(10);
            goto sock_start;
        }

        printf("连接成功\r\n");
        lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);
        g_lwip_connect_state = 1;
        
        while (1)
        {
            recv_data_len = recv(g_sock,g_lwip_demo_recvbuf,
                                 LWIP_DEMO_RX_BUFSIZE,0);
            if (recv_data_len <= 0 )
            {
                closesocket(g_sock);
                g_sock = -1;
                lcd_fill(5, 89, lcddev.width,110, WHITE);
                lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);
                myfree(SRAMIN, tbuf);
                goto sock_start;
            }
            
            /* 接收的数据 */ 
            lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);
            
            if (lwip_err == errQUEUE_FULL)
            {
                printf("队列Key_Queue已满,数据发送失败!\r\n");
            }
            
            vTaskDelay(10);
        }
    }
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;
    
    err_t err;
    
    while (1)
    {
        while (1)
        {
            if(((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) && (g_lwip_connect_state == 1)) /* 有数据要发送 */
            {
                err = write(g_sock, g_lwip_demo_sendbuf, sizeof(g_lwip_demo_sendbuf));
                
                if (err < 0)
                {
                    break;
                }
                
                g_lwip_send_flag &= ~LWIP_SEND_DATA;
            }
            
            vTaskDelay(10);
        }
        
        closesocket(g_sock);
    }
}

Socket 编程 TCP 服务器流程
 

实现 TCP 服务器之前,用户必须先配置结构体 sockaddr_in 的成员变量才能实现TCPServer 连接,该配置步骤如下所示:

① sin_family 设置为 AF_INET 表示 IPv4 网络协议。

② sin_port 为设置端口号。

③ sin_addr.s_addr 设置本地 IP 地址。

④ 调用函数 Socket 创建 Socket 连接,注意:该函数的第二个参数 SOCK_STREAM

示 TCP 连接, SOCK_DGRAM 表示 UDP 连接。

⑤ 调用函数 bind 绑定本地 IP 地址和端口号。

⑥ 调用函数 listen 监听连接请求。

⑦ 调用函数 accept 监听连接。

⑧ 调用收发函数进行通讯。

上述的步骤就是 Socket 编程接口配置 TCPServer 的流程。

Socket 接口的 TCPServer 实验
 

之后应用不要什么goto代码


/* 设置远程IP地址 */
//#define DEST_IP_ADDR0               192
//#define DEST_IP_ADDR1               168
//#define DEST_IP_ADDR2                 1
//#define DEST_IP_ADDR3               167

/* 需要自己设置远程IP地址 */
//#define IP_ADDR   "192.168.1.167"

#define LWIP_DEMO_RX_BUFSIZE         200                        /* 最大接收数据长度 */
#define LWIP_DEMO_PORT               8080                       /* 连接的本地端口号 */
#define LWIP_SEND_THREAD_PRIO       ( tskIDLE_PRIORITY + 3 )    /* 发送数据线程优先级 */

/* 接收数据缓冲区 */
uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE]; 
/* 发送数据内容 */
uint8_t g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";

/* 数据发送标志位 */
uint8_t g_lwip_send_flag;
int g_sock_conn;                          /* 请求的 socked */
int g_lwip_connect_state = 0;
static void lwip_send_thread(void *arg);
extern QueueHandle_t g_display_queue;     /* 显示消息队列句柄 */

/**
 * @brief       发送数据线程
 * @param       无
 * @retval      无
 */
void lwip_data_send(void)
{
    sys_thread_new("lwip_send_thread", lwip_send_thread, NULL, 512, LWIP_SEND_THREAD_PRIO );
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    struct sockaddr_in server_addr; /* 服务器地址 */
    struct sockaddr_in conn_addr;   /* 连接地址 */
    socklen_t addr_len;             /* 地址长度 */
    int err;
    int length;
    int sock_fd;
    char *tbuf;
    BaseType_t lwip_err;
    lwip_data_send();                                    /* 创建一个发送线程 */
    
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* 建立一个新的socket连接 */
    memset(&server_addr, 0, sizeof(server_addr));        /* 将服务器地址清空 */
    server_addr.sin_family = AF_INET;                    /* 地址家族 */
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);     /* 注意转化为网络字节序 */
    server_addr.sin_port = htons(LWIP_DEMO_PORT);        /* 使用SERVER_PORT指定为程序头设定的端口号 */

    tbuf = mymalloc(SRAMIN, 200); /* 申请内存 */
    sprintf((char *)tbuf, "Port:%d", LWIP_DEMO_PORT); /* 客户端端口号 */
    lcd_show_string(5, 150, 200, 16, 16, tbuf, BLUE);
    
    err = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); /* 建立绑定 */

    if (err < 0)                /* 如果绑定失败则关闭套接字 */
    {
        closesocket(sock_fd);   /* 关闭套接字 */
        myfree(SRAMIN, tbuf);
    }

    err = listen(sock_fd, 4);   /* 监听连接请求 */

    if (err < 0)                /* 如果监听失败则关闭套接字 */
    {
        closesocket(sock_fd);   /* 关闭套接字 */
    }
    
    while(1)
    {
        g_lwip_connect_state = 0;
        addr_len = sizeof(struct sockaddr_in); /* 将链接地址赋值给addr_len */

        g_sock_conn = accept(sock_fd, (struct sockaddr *)&conn_addr, &addr_len); /* 对监听到的请求进行连接,状态赋值给sock_conn */

        if (g_sock_conn < 0) /* 状态小于0代表连接故障,此时关闭套接字 */
        {
            closesocket(sock_fd);
        }
        else 
        {
            lcd_show_string(5, 90, 200, 16, 16, "State:Connection Successful", BLUE);
            g_lwip_connect_state = 1;
        }

        while (1)
        {
            memset(g_lwip_demo_recvbuf,0,LWIP_DEMO_RX_BUFSIZE);
            length = recv(g_sock_conn, (unsigned int *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0); /* 将收到的数据放到接收Buff */
            
            if (length <= 0)
            {
                goto atk_exit;
            }
            
//            printf("%s",g_lwip_demo_recvbuf);
            lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);

            if (lwip_err == errQUEUE_FULL)
            {
                printf("队列Key_Queue已满,数据发送失败!\r\n");
            }
        }
atk_exit:
        if (g_sock_conn >= 0)
        {          
            closesocket(g_sock_conn);
            g_sock_conn = -1;
            lcd_fill(5, 89, lcddev.width,110, WHITE);
            lcd_show_string(5, 90, 200, 16, 16, "State:Disconnect", BLUE);
            myfree(SRAMIN, tbuf);
        }
        
    }
}

/**
 * @brief       发送数据线程函数
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
static void lwip_send_thread(void *pvParameters)
{
    pvParameters = pvParameters;
    
    while (1)
    {
        if(((g_lwip_send_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) && (g_lwip_connect_state == 1)) /* 有数据要发送 */
        {
            send(g_sock_conn, g_lwip_demo_sendbuf, sizeof(g_lwip_demo_sendbuf), 0); /* 发送数据 */
            g_lwip_send_flag &= ~LWIP_SEND_DATA;
        }
        
        vTaskDelay(1);
    }
}

Socket 编程接口 TCP 服务器多连接实验

开发板作为服务器,让多个客户端连接

客户端信息的结构体

/* 客户端的信息 */
struct client_info
{
    int socket_num;                 /* socket号的数量 */
    struct sockaddr_in ip_addr;     /* socket客户端的IP地址 */
    int sockaddr_len;               /* socketaddr的长度 */
};

客户端的任务信息结构体

/* 客户端的任务信息 */
struct client_task_info
{
    UBaseType_t client_task_pro;    /* 客户端任务优先级 */
    uint16_t client_task_stk;       /* 客户端任务优先级 */
    TaskHandle_t * client_handler;  /* 客户端任务控制块 */
    char *client_name;              /* 客户端任务名称 */
    char *client_num;               /* 客户端任务数量 */
};

socket信息结构体

/* socket信息 */
struct link_socjet_info
{
    int sock_listen;                /* 监听 */
    int sock_connect;               /* 连接 */
    struct sockaddr_in listen_addr; /* 监听地址 */
    struct sockaddr_in connect_addr;/* 连接地址 */
};

实现多链接的主要函数


/**
 * @brief       客户端的任务函数
 * @param       pvParameters : 传入链接客户端的信息
 * @retval      无
 */
void lwip_client_thread_entry(void *param)
{
    struct client_info* client = param;
    /* 某个客户端连接 */
    printf("Client[%d]%s:%d is connect server\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port));
    /* 向客户端发送连接成功信息 */
    send(client->socket_num, (const void* )send_data, strlen(send_data), 0);
    
    while (1)
    {
        char str[2048];
        memset(str, 0, sizeof(str));
        int bytes = recv(client->socket_num, str, sizeof(str), 0);//客户端发来的数据
        
        /* 获取关闭连接的请求 */
        if (bytes <= 0)
        {
            mem_free(client);
            closesocket(client->socket_num);
            break;//退出这个whiel 删除任务
        }
        
        printf("[%d]%s:%d=>%s...\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port), str);
        
        send((int )client->socket_num, (const void * )str, (size_t )strlen(str), 0);
    }
    
    printf("[%d]%s:%d is disconnect...\r\n", client->socket_num, inet_ntoa(client->ip_addr.sin_addr),ntohs(client->ip_addr.sin_port));
    
    vTaskDelete(NULL); /* 删除该任务 */
}

/**
 * @brief       lwip_demo实验入口
 * @param       无
 * @retval      无
 */
void lwip_demo(void)
{
    struct client_info *client_fo;
    struct client_task_info *client_task_fo;
    struct link_socjet_info *socket_link_info;
    int sin_size = sizeof(struct sockaddr_in);
    char client_name[10] = "cli";
    char client_num[10];
    
    /* socket连接结构体申请内存 */
    socket_link_info = mem_malloc(sizeof(struct link_socjet_info));
    
    /* 设置客户端任务信息 */
    client_task_fo = mem_malloc(sizeof(struct client_task_info));
    client_task_fo->client_handler = NULL;
    client_task_fo->client_task_pro = 5;
    client_task_fo->client_task_stk = 512;
    
    /* 创建socket连接 */
    if ((socket_link_info->sock_listen = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("Socket error\r\n");
        return;
    }

    /* 初始化连接的服务端地址 *///记录每个客户端的ip地址
    socket_link_info->listen_addr.sin_family = AF_INET;
    socket_link_info->listen_addr.sin_port = htons(8088);
    socket_link_info->listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    memset(&(socket_link_info->listen_addr.sin_zero), 0, sizeof(socket_link_info->listen_addr.sin_zero));

    /* 绑定socket和连接的服务端地址信息 */
    if (bind(socket_link_info->sock_listen, (struct sockaddr * )&socket_link_info->listen_addr, sizeof(struct sockaddr)) < 0)
    {
        printf("Bind fail!\r\n");
        goto __exit;
    }

    /* 监听客户端的数量 */
    listen(socket_link_info->sock_listen, 4);
    printf("begin listing...\r\n");

    while (1)
    {
        /* 请求客户端连接 */
        socket_link_info->sock_connect = accept(socket_link_info->sock_listen, (struct sockaddr* )&socket_link_info->connect_addr, (socklen_t* )&sin_size);
        
        if (socket_link_info->sock_connect == -1)
        {
            printf("no socket,waitting others socket disconnect.\r\n");
            continue;
        }
        
        lwip_itoa((char *)socket_link_info->sock_connect, (size_t)client_num, 10);
        strcat(client_name, client_num);
        client_task_fo->client_name = client_name;
        client_task_fo->client_num = client_num;
        
        /* 初始化连接客户端信息 */
        client_fo = mem_malloc(sizeof(struct client_info));
        client_fo->socket_num = socket_link_info->sock_connect;
        memcpy(&client_fo->ip_addr, &socket_link_info->connect_addr, sizeof(struct sockaddr_in));
        client_fo->sockaddr_len = sin_size;
        
        /* 创建连接的客户端任务 */
        xTaskCreate((TaskFunction_t )lwip_client_thread_entry,
                    (const char *   )client_task_fo->client_name,
                    (uint16_t       )client_task_fo->client_task_stk,
                    (void *         )(void*) client_fo,
                    (UBaseType_t    )client_task_fo->client_task_pro ++ ,
                    (TaskHandle_t * )&client_task_fo->client_handler);
        
        if (client_task_fo->client_handler == NULL)
        {

            printf("no memery for thread %s startup failed!\r\n",client_task_fo->client_name);
            mem_free(client_fo);
            continue;
        }
        else
        {
            printf("thread %s success!\r\n", client_task_fo->client_name);
        }
    }
    
__exit: 
    printf("listener failed\r\n");
    /* 关闭这个socket */
    closesocket(socket_link_info->sock_listen);
    vTaskDelete(NULL); /* 删除本任务 */
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380440.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Better Faster Large Language Models via Multi-token Prediction 原理

目录 模型结构&#xff1a; Memory-efficient implementation&#xff1a; 实验&#xff1a; 1. 在大规模模型上效果显著&#xff1a; 2. 在不同类型任务上的效果&#xff1a; 为什么MLP对效果有提升的几点猜测&#xff1a; 1. 并非所有token对生成质量的影响相同 2. 关…

Spring的Validation,这是一套基于注解的权限校验框架

为了保证数据的正确性、完整性&#xff0c;作为一名后端开发工程师&#xff0c;不能仅仅依靠前端来校验数据&#xff0c;还需要对接口请求的参数进行后端的校验。 controller 全局异常处理器 在项目中添加一个全局异常处理器&#xff0c;处理校验异常 RestControllerAdvice p…

MySQL - 如何突破单库性能瓶颈

数据库服务器硬件优化 我们来看看对数据库所在的服务器是如何进行优化的&#xff0c;服务器是数据库的宿主&#xff0c;其性能直接影响了数据库的性能&#xff0c;所以服务器的优化也是数据库优化的第一步。 数据库服务器通常是从 CPU、内存、磁盘三个角度进行硬件优化的&…

apisix透传客户端真实IP(real-ip插件)

文章目录 apisix透传客户端真实IP需求和背景apisix real-ip插件为什么需要 trusted_addresses&#xff1f;安全架构的最佳实践 示例场景apisix界面配置 apisix透传客户端真实IP 需求和背景 当 APISIX 前端有其他反向代理&#xff08;如 Nginx、HAProxy、云厂商的 LB&#xff…

Oracle 数据库的默认隔离级别

Oracle 数据库的默认隔离级别 默认隔离级别&#xff1a;READ COMMITTED Oracle 默认使用 读已提交(READ COMMITTED) 隔离级别&#xff0c;这是大多数OLTP(在线事务处理)系统的标准选择。 官方文档 https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/da…

代码随想录算法训练营第六十四天| 图论9—卡码网47. 参加科学大会,94. 城市间货物运输 I

每日被新算法方式轰炸的一天&#xff0c;今天是dijkstra&#xff08;堆优化版&#xff09;以及Bellman_ford &#xff0c;尝试理解中&#xff0c;属于是只能照着代码大概说一下在干嘛。 47. 参加科学大会 https://kamacoder.com/problempage.php?pid1047 dijkstra&#xff08…

开启健康生活的多元养生之道

健康养生是一门值得终身学习的学问&#xff0c;在追求健康的道路上&#xff0c;除了常见方法&#xff0c;还有许多容易被忽视却同样重要的角度。掌握这些多元养生之道&#xff0c;能让我们的生活更健康、更有品质。​ 室内环境的健康不容忽视。定期清洁空调滤网&#xff0c;避…

【Vite】前端开发服务器的配置

定义一些开发服务器的行为和代理规则 服务器的基本配置 server: {host: true, // 监听所有网络地址port: 8081, // 使用8081端口open: true, // 启动时自动打开浏览器cors: true // 启用CORS跨域支持 } 代理配置 proxy: {/api: {target: https://…

Spring Security与Spring Boot集成原理

Spring Security依赖的是过滤器机制&#xff0c;首先是web容器例如tomcat作为独立的产品&#xff0c;本身有自己的一套过滤器机制用来处理请求&#xff0c;那么如何将tomcat接收到的请求转入到Spring Security的处理逻辑呢&#xff1f;spring充分采用了tomcat的拓展机制提供了t…

VScode各文件转化为PDF的方法

文章目录 代码.py文件.ipynb文本和代码夹杂的文件方法 1:使用 VS Code 插件(推荐)步骤 1:安装必要插件步骤 2:安装 `nbconvert`步骤 3:间接导出(HTML → PDF)本文遇见了系列错误:解决方案:问题原因步骤 1:降级 Jinja2 至兼容版本步骤 2:确保 nbconvert 版本兼容替代…

Vue3学习(组合式API——Watch侦听器、watchEffect()详解)

目录 一、Watch侦听器。 &#xff08;1&#xff09;侦听单个数据。 &#xff08;2&#xff09;侦听多个数据。&#xff08;数组写法&#xff1f;&#xff01;&#xff09; &#xff08;3&#xff09;immediate参数。(立即执行回调) &#xff08;3&#xff09;deep参数。(深层监…

【node.js】安装与配置

个人主页&#xff1a;Guiat 归属专栏&#xff1a;node.js 文章目录 1. Node.js简介1.1 Node.js的特点1.2 Node.js架构 2. Node.js安装2.1 下载和安装方法2.1.1 Windows安装2.1.2 macOS安装2.1.3 Linux安装 2.2 使用NVM安装和管理Node.js版本2.2.1 安装NVM2.2.2 使用NVM管理Node…

《AI大模型应知应会100篇》第62篇:TypeChat——类型安全的大模型编程框架

第62篇&#xff1a;TypeChat——类型安全的大模型编程框架 摘要 在构建 AI 应用时&#xff0c;一个常见的痛点是大语言模型&#xff08;LLM&#xff09;输出的不确定性与格式不一致问题。开发者往往需要手动解析、校验和处理模型返回的内容&#xff0c;这不仅增加了开发成本&a…

EdgeShard:通过协作边缘计算实现高效的 LLM 推理

(2024-05-23) EdgeShard: Efficient LLM Inference via Collaborative Edge Computing (EdgeShard:通过协作边缘计算实现高效的 LLM 推理) 作者: Mingjin Zhang; Jiannong Cao; Xiaoming Shen; Zeyang Cui;期刊: (发表日期: 2024-05-23)期刊分区:本地链接: Zhang 等 - 2024 …

火山 RTC 引擎9 ----集成 appkey

一、集成 appkey 1、网易RTC 初始化过程 1&#xff09;、添加头文件 实现互动直播 - 互动直播 2.0网易云信互动直播产品的基本功能包括音视频通话和连麦直播&#xff0c;当您成功初始化 SDK 之后&#xff0c;您可以简单体验本产品的基本业务流程&#xff0c;例如主播加入房间…

Adminer:一个基于Web的轻量级数据库管理工具

Adminer 是一个由单个 PHP 文件实现的免费数据库管理工具&#xff0c;支持 MySQL、MariaDB、PostgreSQL、CockroachDB、SQLite、SQL Server、Oracle、Elasticsearch、SimpleDB、MongoDB、Firebird、Clickhouse 等数据库。 Adminer 支持的主要功能如下&#xff1a; 连接数据库服…

RK3568下QT实现按钮切换tabWidget

运行效果: 在 Qt 应用程序开发过程中,TabWidget 是一种非常实用的 UI 组件,它能够以选项卡的形式展示多个页面内容,帮助我们有效组织和管理复杂的界面布局。而在实际使用时,常常会有通过按钮点击来切换 TabWidget 页面的需求,本文将通过一个完整的示例,详细介绍如何在 Q…

2025 OceanBase 开发者大会全议程指南

5 月 17 日&#xff0c;第三届 OceanBase 开发者大会将在广州举办。 我们邀请数据库领军者与AI实践先锋&#xff0c;与开发者一起探讨数据库与 AI 协同创新的技术趋势&#xff0c;面对面交流 OceanBase 在 TP、AP、KV 及 AI 能力上的最新进展&#xff0c;深度体验“打破技术栈…

day017-磁盘管理-实战

文章目录 1. 硬盘命名规则2. 添加硬盘2.1 查看硬盘名称 3. 硬盘分区3.1 分区命名规则&#xff1a;mbr分区表格式3.2 创建分区&#xff1a;fdisk3.2.1 fdisk -l&#xff1a;查看硬盘及分区信息3.2.2 fdisk /dev/sdc :为该硬盘分区3.2.3 创建扩展分区和逻辑分区3.2.4 保存设置并退…

【成品设计】STM32和UCOS-II的项目

项目1&#xff1a;《基于STM32和UCOS-II的水质监测系统》 Ps&#xff1a;分为带系统版本和不带系统版本&#xff0c;功能都一样。 功能说明&#xff1a; 1. 单片机主控&#xff1a;STM32F103C8T6单片机作为核心控制。 2. 酸碱度传感器&#xff1a;实时采集当前PH值。 3. 水质…