LWIP的Socket接口

Socket****接口简介

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

netconn是对RAW的封装

Socket是对netconn的封装

SOCKET结构体

cpp 复制代码
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 进行绑定。

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

实现连接的主要函数

cpp 复制代码
/* 需要自己设置远程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协议 链接检测*/

组播的主要实现函数

cpp 复制代码
/* 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 实验

实现的主要代码

cpp 复制代码
/* 设置远程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代码

cpp 复制代码
/* 设置远程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 服务器多连接实验

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

客户端信息的结构体

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

客户端的任务信息结构体

cpp 复制代码
/* 客户端的任务信息 */
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信息结构体

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

实现多链接的主要函数

cpp 复制代码
/**
 * @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); /* 删除本任务 */
}
相关推荐
大树887 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠7 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质8 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
小宇宙Zz8 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工9 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
网络研究院9 小时前
2026年网络安全
网络·安全·法律·法规·趋势·发展
酣大智9 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
treesforest9 小时前
AI安全系统如何识别异常访问?IP风险识别正在成为关键能力
网络·人工智能·tcp/ip·安全·web安全
shushangyun_9 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
古城小栈9 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix